1 /* 2 * Copyright (C) 2019 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.server.pm.permission; 18 19 import android.annotation.NonNull; 20 import android.app.ActivityManager; 21 import android.app.ActivityManagerInternal; 22 import android.app.AlarmManager; 23 import android.app.IActivityManager; 24 import android.app.IUidObserver; 25 import android.app.UidObserver; 26 import android.content.BroadcastReceiver; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.content.pm.PackageManager; 31 import android.os.Handler; 32 import android.os.RemoteException; 33 import android.permission.PermissionControllerManager; 34 import android.provider.DeviceConfig; 35 import android.util.Log; 36 import android.util.SparseArray; 37 38 import com.android.internal.annotations.GuardedBy; 39 import com.android.server.LocalServices; 40 import com.android.server.PermissionThread; 41 42 /** 43 * Class that handles one-time permissions for a user 44 */ 45 public class OneTimePermissionUserManager { 46 47 private static final String LOG_TAG = OneTimePermissionUserManager.class.getSimpleName(); 48 49 private static final boolean DEBUG = false; 50 private static final long DEFAULT_KILLED_DELAY_MILLIS = 5000; 51 public static final String PROPERTY_KILLED_DELAY_CONFIG_KEY = 52 "one_time_permissions_killed_delay_millis"; 53 54 private final @NonNull Context mContext; 55 private final @NonNull IActivityManager mIActivityManager; 56 private final @NonNull ActivityManagerInternal mActivityManagerInternal; 57 private final @NonNull AlarmManager mAlarmManager; 58 private final @NonNull PermissionControllerManager mPermissionControllerManager; 59 60 private final Object mLock = new Object(); 61 62 private final BroadcastReceiver mUninstallListener = new BroadcastReceiver() { 63 @Override 64 public void onReceive(Context context, Intent intent) { 65 if (Intent.ACTION_UID_REMOVED.equals(intent.getAction())) { 66 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); 67 PackageInactivityListener listener = mListeners.get(uid); 68 if (listener != null) { 69 if (DEBUG) { 70 Log.d(LOG_TAG, "Removing the inactivity listener for " + uid); 71 } 72 listener.cancel(); 73 mListeners.remove(uid); 74 } 75 } 76 } 77 }; 78 79 /** Maps the uid to the PackageInactivityListener */ 80 @GuardedBy("mLock") 81 private final SparseArray<PackageInactivityListener> mListeners = new SparseArray<>(); 82 private final Handler mHandler; 83 OneTimePermissionUserManager(@onNull Context context)84 OneTimePermissionUserManager(@NonNull Context context) { 85 mContext = context; 86 mIActivityManager = ActivityManager.getService(); 87 mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); 88 mAlarmManager = context.getSystemService(AlarmManager.class); 89 mPermissionControllerManager = new PermissionControllerManager( 90 mContext, PermissionThread.getHandler()); 91 mHandler = context.getMainThreadHandler(); 92 } 93 startPackageOneTimeSession(@onNull String packageName, int deviceId, long timeoutMillis, long revokeAfterKilledDelayMillis)94 void startPackageOneTimeSession(@NonNull String packageName, int deviceId, long timeoutMillis, 95 long revokeAfterKilledDelayMillis) { 96 int uid; 97 try { 98 uid = mContext.getPackageManager().getPackageUid(packageName, 0); 99 } catch (PackageManager.NameNotFoundException e) { 100 Log.e(LOG_TAG, 101 "Unknown package name " + packageName + ", device ID " + deviceId, e); 102 return; 103 } 104 105 synchronized (mLock) { 106 PackageInactivityListener listener = mListeners.get(uid); 107 if (listener != null) { 108 listener.updateSessionParameters(timeoutMillis, revokeAfterKilledDelayMillis); 109 return; 110 } 111 listener = new PackageInactivityListener(uid, packageName, deviceId, timeoutMillis, 112 revokeAfterKilledDelayMillis); 113 mListeners.put(uid, listener); 114 } 115 } 116 117 /** 118 * Stops the one-time permission session for the package. The callback to the end of session is 119 * not invoked. If there is no one-time session for the package then nothing happens. 120 * 121 * @param packageName Package to stop the one-time permission session for 122 */ stopPackageOneTimeSession(@onNull String packageName)123 void stopPackageOneTimeSession(@NonNull String packageName) { 124 int uid; 125 try { 126 uid = mContext.getPackageManager().getPackageUid(packageName, 0); 127 } catch (PackageManager.NameNotFoundException e) { 128 Log.e(LOG_TAG, "Unknown package name " + packageName, e); 129 return; 130 } 131 132 synchronized (mLock) { 133 PackageInactivityListener listener = mListeners.get(uid); 134 if (listener != null) { 135 mListeners.remove(uid); 136 listener.cancel(); 137 } 138 } 139 } 140 141 /** 142 * Register to listen for Uids being uninstalled. This must be done outside of the 143 * PermissionManagerService lock. 144 */ registerUninstallListener()145 void registerUninstallListener() { 146 mContext.registerReceiver(mUninstallListener, new IntentFilter(Intent.ACTION_UID_REMOVED)); 147 } 148 149 /** 150 * A class which watches a package for inactivity and notifies the permission controller when 151 * the package becomes inactive 152 */ 153 private class PackageInactivityListener implements AlarmManager.OnAlarmListener { 154 155 private static final long TIMER_INACTIVE = -1; 156 157 private static final int STATE_GONE = 0; 158 private static final int STATE_TIMER = 1; 159 private static final int STATE_ACTIVE = 2; 160 161 private final int mUid; 162 private final @NonNull String mPackageName; 163 private final int mDeviceId; 164 private long mTimeout; 165 private long mRevokeAfterKilledDelay; 166 167 private boolean mIsAlarmSet; 168 private boolean mIsFinished; 169 170 private long mTimerStart = TIMER_INACTIVE; 171 172 private final Object mInnerLock = new Object(); 173 private final Object mToken = new Object(); 174 private final IUidObserver mObserver = new UidObserver() { 175 @Override 176 public void onUidGone(int uid, boolean disabled) { 177 if (uid == mUid) { 178 PackageInactivityListener.this.updateUidState(STATE_GONE); 179 } 180 } 181 182 @Override 183 public void onUidStateChanged(int uid, int procState, long procStateSeq, 184 int capability) { 185 if (uid == mUid) { 186 if (procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE 187 && procState != ActivityManager.PROCESS_STATE_NONEXISTENT) { 188 PackageInactivityListener.this.updateUidState(STATE_TIMER); 189 } else { 190 PackageInactivityListener.this.updateUidState(STATE_ACTIVE); 191 } 192 } 193 } 194 }; 195 PackageInactivityListener(int uid, @NonNull String packageName, int deviceId, long timeout, long revokeAfterkilledDelay)196 private PackageInactivityListener(int uid, @NonNull String packageName, int deviceId, 197 long timeout, long revokeAfterkilledDelay) { 198 Log.i(LOG_TAG, 199 "Start tracking " + packageName + ". uid=" + uid + " timeout=" + timeout 200 + " killedDelay=" + revokeAfterkilledDelay); 201 202 mUid = uid; 203 mPackageName = packageName; 204 mDeviceId = deviceId; 205 mTimeout = timeout; 206 mRevokeAfterKilledDelay = revokeAfterkilledDelay == -1 207 ? DeviceConfig.getLong( 208 DeviceConfig.NAMESPACE_PERMISSIONS, PROPERTY_KILLED_DELAY_CONFIG_KEY, 209 DEFAULT_KILLED_DELAY_MILLIS) 210 : revokeAfterkilledDelay; 211 212 try { 213 mIActivityManager.registerUidObserver(mObserver, 214 ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_PROCSTATE, 215 ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 216 null); 217 } catch (RemoteException e) { 218 Log.e(LOG_TAG, "Couldn't check uid proc state", e); 219 // Can't register uid observer, just revoke immediately 220 synchronized (mInnerLock) { 221 onPackageInactiveLocked(); 222 } 223 } 224 225 updateUidState(); 226 } 227 updateSessionParameters(long timeoutMillis, long revokeAfterKilledDelayMillis)228 public void updateSessionParameters(long timeoutMillis, long revokeAfterKilledDelayMillis) { 229 synchronized (mInnerLock) { 230 mTimeout = Math.min(mTimeout, timeoutMillis); 231 mRevokeAfterKilledDelay = Math.min(mRevokeAfterKilledDelay, 232 revokeAfterKilledDelayMillis == -1 233 ? DeviceConfig.getLong( 234 DeviceConfig.NAMESPACE_PERMISSIONS, 235 PROPERTY_KILLED_DELAY_CONFIG_KEY, DEFAULT_KILLED_DELAY_MILLIS) 236 : revokeAfterKilledDelayMillis); 237 Log.v(LOG_TAG, 238 "Updated params for " + mPackageName + ", device ID " + mDeviceId 239 + ". timeout=" + mTimeout 240 + " killedDelay=" + mRevokeAfterKilledDelay); 241 updateUidState(); 242 } 243 } 244 getCurrentState()245 private int getCurrentState() { 246 return getStateFromProcState(mActivityManagerInternal.getUidProcessState(mUid)); 247 } 248 getStateFromProcState(int procState)249 private int getStateFromProcState(int procState) { 250 if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT) { 251 return STATE_GONE; 252 } else { 253 if (procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { 254 return STATE_TIMER; 255 } else { 256 return STATE_ACTIVE; 257 } 258 } 259 } 260 updateUidState()261 private void updateUidState() { 262 updateUidState(getCurrentState()); 263 } 264 updateUidState(int state)265 private void updateUidState(int state) { 266 Log.v(LOG_TAG, "Updating state for " + mPackageName + " (" + mUid + ")." 267 + " device ID=" + mDeviceId + ", state=" + state); 268 synchronized (mInnerLock) { 269 // Remove any pending inactivity callback 270 mHandler.removeCallbacksAndMessages(mToken); 271 272 if (state == STATE_GONE) { 273 if (mRevokeAfterKilledDelay == 0) { 274 onPackageInactiveLocked(); 275 return; 276 } 277 // Delay revocation in case app is restarting 278 mHandler.postDelayed(() -> { 279 int currentState; 280 synchronized (mInnerLock) { 281 currentState = getCurrentState(); 282 if (currentState == STATE_GONE) { 283 onPackageInactiveLocked(); 284 return; 285 } 286 } 287 if (DEBUG) { 288 Log.d(LOG_TAG, "No longer gone after delayed revocation. " 289 + "Rechecking for " + mPackageName + " (" + mUid 290 + "). device ID " + mDeviceId); 291 } 292 updateUidState(currentState); 293 }, mToken, mRevokeAfterKilledDelay); 294 return; 295 } else if (state == STATE_TIMER) { 296 if (mTimerStart == TIMER_INACTIVE) { 297 if (DEBUG) { 298 Log.d(LOG_TAG, "Start the timer for " 299 + mPackageName + " (" + mUid + "). device ID " + mDeviceId); 300 } 301 mTimerStart = System.currentTimeMillis(); 302 setAlarmLocked(); 303 } 304 } else if (state == STATE_ACTIVE) { 305 mTimerStart = TIMER_INACTIVE; 306 cancelAlarmLocked(); 307 } 308 } 309 } 310 311 /** 312 * Stop watching the package for inactivity 313 */ cancel()314 private void cancel() { 315 synchronized (mInnerLock) { 316 mIsFinished = true; 317 cancelAlarmLocked(); 318 try { 319 mIActivityManager.unregisterUidObserver(mObserver); 320 } catch (RemoteException e) { 321 Log.e(LOG_TAG, "Unable to unregister uid observer.", e); 322 } 323 } 324 } 325 326 /** 327 * Set the alarm which will callback when the package is inactive 328 */ 329 @GuardedBy("mInnerLock") setAlarmLocked()330 private void setAlarmLocked() { 331 if (mIsAlarmSet) { 332 return; 333 } 334 335 if (DEBUG) { 336 Log.d(LOG_TAG, "Scheduling alarm for " + mPackageName + " (" + mUid + ")." 337 + " device ID " + mDeviceId); 338 } 339 long revokeTime = mTimerStart + mTimeout; 340 if (revokeTime > System.currentTimeMillis()) { 341 mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, revokeTime, LOG_TAG, this, 342 mHandler); 343 mIsAlarmSet = true; 344 } else { 345 mIsAlarmSet = true; 346 onAlarm(); 347 } 348 } 349 350 /** 351 * Cancel the alarm 352 */ 353 @GuardedBy("mInnerLock") cancelAlarmLocked()354 private void cancelAlarmLocked() { 355 if (mIsAlarmSet) { 356 if (DEBUG) { 357 Log.d(LOG_TAG, "Canceling alarm for " + mPackageName + " (" + mUid + ")." 358 + " device ID " + mDeviceId); 359 } 360 mAlarmManager.cancel(this); 361 mIsAlarmSet = false; 362 } 363 } 364 365 /** 366 * Called when the package is considered inactive. This is the end of the session 367 */ 368 @GuardedBy("mInnerLock") onPackageInactiveLocked()369 private void onPackageInactiveLocked() { 370 if (mIsFinished) { 371 return; 372 } 373 if (DEBUG) { 374 Log.d(LOG_TAG, "onPackageInactiveLocked stack trace for " 375 + mPackageName + " (" + mUid + "). device ID " + mDeviceId, 376 new RuntimeException()); 377 } 378 mIsFinished = true; 379 cancelAlarmLocked(); 380 mHandler.post( 381 () -> { 382 Log.i(LOG_TAG, "One time session expired for " 383 + mPackageName + " (" + mUid + "). deviceID " + mDeviceId); 384 mPermissionControllerManager.notifyOneTimePermissionSessionTimeout( 385 mPackageName, mDeviceId); 386 }); 387 try { 388 mIActivityManager.unregisterUidObserver(mObserver); 389 } catch (RemoteException e) { 390 Log.e(LOG_TAG, "Unable to unregister uid observer.", e); 391 } 392 synchronized (mLock) { 393 mListeners.remove(mUid); 394 } 395 } 396 397 @Override onAlarm()398 public void onAlarm() { 399 if (DEBUG) { 400 Log.d(LOG_TAG, "Alarm received for " + mPackageName + " (" + mUid + ")." 401 + " device ID " + mDeviceId); 402 } 403 synchronized (mInnerLock) { 404 if (!mIsAlarmSet) { 405 return; 406 } 407 mIsAlarmSet = false; 408 onPackageInactiveLocked(); 409 } 410 } 411 } 412 } 413