1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.log; 18 19 import static android.app.StatusBarManager.ALL_SESSIONS; 20 import static android.app.StatusBarManager.SESSION_BIOMETRIC_PROMPT; 21 import static android.app.StatusBarManager.SESSION_KEYGUARD; 22 23 import android.annotation.Nullable; 24 import android.os.RemoteException; 25 import android.util.Log; 26 27 import androidx.annotation.NonNull; 28 29 import com.android.internal.logging.InstanceId; 30 import com.android.internal.logging.InstanceIdSequence; 31 import com.android.internal.logging.UiEvent; 32 import com.android.internal.logging.UiEventLogger; 33 import com.android.internal.statusbar.IStatusBarService; 34 import com.android.keyguard.KeyguardUpdateMonitor; 35 import com.android.keyguard.KeyguardUpdateMonitorCallback; 36 import com.android.systemui.CoreStartable; 37 import com.android.systemui.biometrics.AuthController; 38 import com.android.systemui.dagger.SysUISingleton; 39 import com.android.systemui.statusbar.policy.KeyguardStateController; 40 41 import java.io.PrintWriter; 42 import java.util.HashMap; 43 import java.util.Map; 44 45 import javax.inject.Inject; 46 47 /** 48 * Track Session InstanceIds to be used for metrics logging to correlate logs in the same 49 * session. Can be used across processes via StatusBarManagerService#registerSessionListener 50 */ 51 @SysUISingleton 52 public class SessionTracker implements CoreStartable { 53 private static final String TAG = "SessionTracker"; 54 55 // To enable logs: `adb shell setprop log.tag.SessionTracker DEBUG` & restart sysui 56 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 57 58 // At most 20 bits: ~1m possibilities, ~0.5% probability of collision in 100 values 59 private final InstanceIdSequence mInstanceIdGenerator = new InstanceIdSequence(1 << 20); 60 61 private final IStatusBarService mStatusBarManagerService; 62 private final AuthController mAuthController; 63 private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; 64 private final KeyguardStateController mKeyguardStateController; 65 private final UiEventLogger mUiEventLogger; 66 private final Map<Integer, InstanceId> mSessionToInstanceId = new HashMap<>(); 67 68 private boolean mKeyguardSessionStarted; 69 70 @Inject SessionTracker( IStatusBarService statusBarService, AuthController authController, KeyguardUpdateMonitor keyguardUpdateMonitor, KeyguardStateController keyguardStateController, UiEventLogger uiEventLogger )71 public SessionTracker( 72 IStatusBarService statusBarService, 73 AuthController authController, 74 KeyguardUpdateMonitor keyguardUpdateMonitor, 75 KeyguardStateController keyguardStateController, 76 UiEventLogger uiEventLogger 77 ) { 78 mStatusBarManagerService = statusBarService; 79 mAuthController = authController; 80 mKeyguardUpdateMonitor = keyguardUpdateMonitor; 81 mKeyguardStateController = keyguardStateController; 82 mUiEventLogger = uiEventLogger; 83 } 84 85 @Override start()86 public void start() { 87 mAuthController.addCallback(mAuthControllerCallback); 88 mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); 89 mKeyguardStateController.addCallback(mKeyguardStateCallback); 90 91 if (mKeyguardStateController.isShowing()) { 92 mKeyguardSessionStarted = true; 93 startSession(SESSION_KEYGUARD); 94 } 95 } 96 97 /** 98 * Get the session ID associated with the passed session type. 99 */ getSessionId(int type)100 public @Nullable InstanceId getSessionId(int type) { 101 return mSessionToInstanceId.getOrDefault(type, null); 102 } 103 startSession(int type)104 private void startSession(int type) { 105 if (mSessionToInstanceId.getOrDefault(type, null) != null) { 106 Log.e(TAG, "session [" + getString(type) + "] was already started"); 107 return; 108 } 109 110 final InstanceId instanceId = mInstanceIdGenerator.newInstanceId(); 111 mSessionToInstanceId.put(type, instanceId); 112 try { 113 if (DEBUG) { 114 Log.d(TAG, "Session start for [" + getString(type) + "] id=" + instanceId); 115 } 116 mStatusBarManagerService.onSessionStarted(type, instanceId); 117 } catch (RemoteException e) { 118 Log.e(TAG, "Unable to send onSessionStarted for session=" 119 + "[" + getString(type) + "]", e); 120 } 121 } 122 endSession(int type)123 private void endSession(int type) { 124 endSession(type, null); 125 } 126 endSession(int type, @Nullable SessionUiEvent endSessionUiEvent)127 private void endSession(int type, @Nullable SessionUiEvent endSessionUiEvent) { 128 if (mSessionToInstanceId.getOrDefault(type, null) == null) { 129 Log.e(TAG, "session [" + getString(type) + "] was not started"); 130 return; 131 } 132 133 final InstanceId instanceId = mSessionToInstanceId.get(type); 134 mSessionToInstanceId.put(type, null); 135 try { 136 if (DEBUG) { 137 Log.d(TAG, "Session end for [" + getString(type) + "] id=" + instanceId); 138 } 139 if (endSessionUiEvent != null) { 140 mUiEventLogger.log(endSessionUiEvent, instanceId); 141 } 142 mStatusBarManagerService.onSessionEnded(type, instanceId); 143 } catch (RemoteException e) { 144 Log.e(TAG, "Unable to send onSessionEnded for session=" 145 + "[" + getString(type) + "]", e); 146 } 147 } 148 149 public KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = 150 new KeyguardUpdateMonitorCallback() { 151 @Override 152 public void onStartedGoingToSleep(int why) { 153 if (mKeyguardSessionStarted) { 154 endSession(SESSION_KEYGUARD, SessionUiEvent.KEYGUARD_SESSION_END_GOING_TO_SLEEP); 155 } 156 157 // Start a new session whenever the device goes to sleep 158 mKeyguardSessionStarted = true; 159 startSession(SESSION_KEYGUARD); 160 } 161 }; 162 163 164 public KeyguardStateController.Callback mKeyguardStateCallback = 165 new KeyguardStateController.Callback() { 166 public void onKeyguardShowingChanged() { 167 boolean wasSessionStarted = mKeyguardSessionStarted; 168 boolean keyguardShowing = mKeyguardStateController.isShowing(); 169 if (keyguardShowing && !wasSessionStarted) { 170 // the keyguard can start showing without the device going to sleep (ie: lockdown 171 // from the power button), so we start a new keyguard session when the keyguard is 172 // newly shown in addition to when the device starts going to sleep 173 mKeyguardSessionStarted = true; 174 startSession(SESSION_KEYGUARD); 175 } else if (!keyguardShowing && wasSessionStarted) { 176 mKeyguardSessionStarted = false; 177 endSession(SESSION_KEYGUARD, 178 SessionUiEvent.KEYGUARD_SESSION_END_KEYGUARD_GOING_AWAY); 179 } 180 } 181 }; 182 183 public AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { 184 @Override 185 public void onBiometricPromptShown() { 186 startSession(SESSION_BIOMETRIC_PROMPT); 187 } 188 189 @Override 190 public void onBiometricPromptDismissed() { 191 endSession(SESSION_BIOMETRIC_PROMPT); 192 } 193 }; 194 195 @Override dump(@onNull PrintWriter pw, @NonNull String[] args)196 public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { 197 for (int session : ALL_SESSIONS) { 198 pw.println(" " + getString(session) 199 + " instanceId=" + mSessionToInstanceId.get(session)); 200 } 201 } 202 203 /** 204 * @return the string representation of a SINGLE SessionFlag. Combined SessionFlags will be 205 * considered unknown. 206 */ getString(int sessionType)207 public static String getString(int sessionType) { 208 if (sessionType == SESSION_KEYGUARD) { 209 return "KEYGUARD"; 210 } else if (sessionType == SESSION_BIOMETRIC_PROMPT) { 211 return "BIOMETRIC_PROMPT"; 212 } 213 214 return "unknownType=" + sessionType; 215 } 216 217 enum SessionUiEvent implements UiEventLogger.UiEventEnum { 218 @UiEvent(doc = "A keyguard session ended due to the keyguard going away.") 219 KEYGUARD_SESSION_END_KEYGUARD_GOING_AWAY(1354), 220 221 @UiEvent(doc = "A keyguard session ended due to display going to sleep.") 222 KEYGUARD_SESSION_END_GOING_TO_SLEEP(1355); 223 224 private final int mId; SessionUiEvent(int id)225 SessionUiEvent(int id) { 226 mId = id; 227 } 228 229 @Override getId()230 public int getId() { 231 return mId; 232 } 233 } 234 } 235