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