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