OSDN Git Service

Notify AppOpsService of UserRestrictions and Owners
authorJason Monk <jmonk@google.com>
Tue, 6 May 2014 13:55:28 +0000 (09:55 -0400)
committerJason Monk <jmonk@google.com>
Fri, 16 May 2014 17:12:41 +0000 (13:12 -0400)
This makes the DevicePolicyManagerService and UserManagerService
push the DeviceOwner/ProfileOwners and user restrictions on boot
as well as on any change.

This also adds a list of restrictions that allow any op to connected with
a user restriction such that it will return MODE_IGNORED when the user
restriction is present (except for the device/profile owner).

Change-Id: Id8a9591d8f04fe5ecebd95750d9010afc0cd786c

core/java/android/app/AppOpsManager.java
core/java/com/android/internal/app/IAppOpsService.aidl
services/core/java/com/android/server/AppOpsService.java
services/core/java/com/android/server/pm/UserManagerService.java
services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java

index d813dab..62c4f0f 100644 (file)
@@ -19,6 +19,7 @@ package android.app;
 import android.Manifest;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.UserManager;
 import android.util.ArrayMap;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IAppOpsCallback;
@@ -412,6 +413,58 @@ public class AppOpsManager {
     };
 
     /**
+     * Specifies whether an Op should be restricted by a user restriction.
+     * Each Op should be filled with a restriction string from UserManager or
+     * null to specify it is not affected by any user restriction.
+     */
+    private static String[] sOpRestrictions = new String[] {
+            null, //COARSE_LOCATION
+            null, //FINE_LOCATION
+            null, //GPS
+            null, //VIBRATE
+            null, //READ_CONTACTS
+            null, //WRITE_CONTACTS
+            null, //READ_CALL_LOG
+            null, //WRITE_CALL_LOG
+            null, //READ_CALENDAR
+            null, //WRITE_CALENDAR
+            null, //WIFI_SCAN
+            null, //POST_NOTIFICATION
+            null, //NEIGHBORING_CELLS
+            null, //CALL_PHONE
+            null, //READ_SMS
+            null, //WRITE_SMS
+            null, //RECEIVE_SMS
+            null, //RECEIVE_EMERGECY_SMS
+            null, //RECEIVE_MMS
+            null, //RECEIVE_WAP_PUSH
+            null, //SEND_SMS
+            null, //READ_ICC_SMS
+            null, //WRITE_ICC_SMS
+            null, //WRITE_SETTINGS
+            null, //SYSTEM_ALERT_WINDOW
+            null, //ACCESS_NOTIFICATIONS
+            null, //CAMERA
+            null, //RECORD_AUDIO
+            null, //PLAY_AUDIO
+            null, //READ_CLIPBOARD
+            null, //WRITE_CLIPBOARD
+            null, //TAKE_MEDIA_BUTTONS
+            null, //TAKE_AUDIO_FOCUS
+            null, //AUDIO_MASTER_VOLUME
+            null, //AUDIO_VOICE_VOLUME
+            null, //AUDIO_RING_VOLUME
+            null, //AUDIO_MEDIA_VOLUME
+            null, //AUDIO_ALARM_VOLUME
+            null, //AUDIO_NOTIFICATION_VOLUME
+            null, //AUDIO_BLUETOOTH_VOLUME
+            null, //WAKE_LOCK
+            null, //MONITOR_LOCATION
+            null, //MONITOR_HIGH_POWER_LOCATION
+            null, //GET_USAGE_STATS
+    };
+
+    /**
      * This specifies the default mode for each operation.
      */
     private static int[] sOpDefaultMode = new int[] {
@@ -542,6 +595,10 @@ public class AppOpsManager {
             throw new IllegalStateException("sOpDisableReset length " + sOpDisableReset.length
                     + " should be " + _NUM_OP);
         }
+        if (sOpRestrictions.length != _NUM_OP) {
+            throw new IllegalStateException("sOpRestrictions length " + sOpRestrictions.length
+                    + " should be " + _NUM_OP);
+        }
         for (int i=0; i<_NUM_OP; i++) {
             if (sOpToString[i] != null) {
                 sOpStrToOp.put(sOpToString[i], i);
@@ -575,6 +632,14 @@ public class AppOpsManager {
     }
 
     /**
+     * Retrieve the user restriction associated with an operation, or null if there is not one.
+     * @hide
+     */
+    public static String opToRestriction(int op) {
+        return sOpRestrictions[op];
+    }
+
+    /**
      * Retrieve the default mode for the operation.
      * @hide
      */
index cd75010..8e6fa58 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.internal.app;
 
 import android.app.AppOpsManager;
+import android.os.Bundle;
 import com.android.internal.app.IAppOpsCallback;
 
 interface IAppOpsService {
@@ -38,4 +39,10 @@ interface IAppOpsService {
     void resetAllModes();
     int checkAudioOperation(int code, int stream, int uid, String packageName);
     void setAudioRestriction(int code, int stream, int uid, int mode, in String[] exceptionPackages);
+
+    void setDeviceOwner(String packageName);
+    void setProfileOwner(String packageName, int userHandle);
+    void setUserRestrictions(in Bundle restrictions, int userHandle);
+    void removeUser(int userHandle);
+
 }
index e26747c..be20616 100644 (file)
@@ -36,12 +36,14 @@ import android.content.pm.PackageManager.NameNotFoundException;
 import android.media.AudioService;
 import android.os.AsyncTask;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
@@ -49,6 +51,7 @@ import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 
@@ -56,6 +59,7 @@ import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
+import com.google.android.util.AbstractMessageParser.MusicTrack;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -91,6 +95,10 @@ public class AppOpsService extends IAppOpsService.Stub {
     final SparseArray<HashMap<String, Ops>> mUidOps
             = new SparseArray<HashMap<String, Ops>>();
 
+    private int mDeviceOwnerUid;
+    private final SparseIntArray mProfileOwnerUids = new SparseIntArray();
+    private final SparseArray<boolean[]> mOpRestrictions = new SparseArray<boolean[]>();
+
     public final static class Ops extends SparseArray<Op> {
         public final String packageName;
         public final int uid;
@@ -548,6 +556,9 @@ public class AppOpsService extends IAppOpsService.Stub {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
         synchronized (this) {
+            if (isOpRestricted(uid, code)) {
+                return AppOpsManager.MODE_IGNORED;
+            }
             Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
             if (op == null) {
                 return AppOpsManager.opToDefaultMode(code);
@@ -631,6 +642,9 @@ public class AppOpsService extends IAppOpsService.Stub {
                 return AppOpsManager.MODE_ERRORED;
             }
             Op op = getOpLocked(ops, code, true);
+            if (isOpRestricted(uid, code)) {
+                return AppOpsManager.MODE_IGNORED;
+            }
             if (op.duration == -1) {
                 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
                         + " code " + code + " time=" + op.time + " duration=" + op.duration);
@@ -665,6 +679,9 @@ public class AppOpsService extends IAppOpsService.Stub {
                 return AppOpsManager.MODE_ERRORED;
             }
             Op op = getOpLocked(ops, code, true);
+            if (isOpRestricted(uid, code)) {
+                return AppOpsManager.MODE_IGNORED;
+            }
             final int switchCode = AppOpsManager.opToSwitch(code);
             final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
             if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
@@ -830,6 +847,23 @@ public class AppOpsService extends IAppOpsService.Stub {
         return op;
     }
 
+    private boolean isOpRestricted(int uid, int code) {
+        int userHandle = UserHandle.getUserId(uid);
+        boolean[] opRestrictions = mOpRestrictions.get(userHandle);
+        if ((opRestrictions != null) && opRestrictions[code]) {
+            if (userHandle == UserHandle.USER_OWNER) {
+                if (uid != mDeviceOwnerUid) {
+                    return true;
+                }
+            } else {
+                if (uid != mProfileOwnerUids.get(userHandle, -1)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     void readState() {
         synchronized (mFile) {
             synchronized (this) {
@@ -1167,4 +1201,66 @@ public class AppOpsService extends IAppOpsService.Stub {
         int mode;
         ArraySet<String> exceptionPackages = NO_EXCEPTIONS;
     }
+
+    @Override
+    public void setDeviceOwner(String packageName) throws RemoteException {
+        checkSystemUid("setDeviceOwner");
+        try {
+            mDeviceOwnerUid = mContext.getPackageManager().getPackageUid(packageName,
+                    UserHandle.USER_OWNER);
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Could not find Device Owner UID");
+            mDeviceOwnerUid = -1;
+            throw new IllegalArgumentException("Could not find device owner package "
+                    + packageName);
+        }
+    }
+
+    @Override
+    public void setProfileOwner(String packageName, int userHandle) throws RemoteException {
+        checkSystemUid("setProfileOwner");
+        try {
+            int uid = mContext.getPackageManager().getPackageUid(packageName,
+                    userHandle);
+            mProfileOwnerUids.put(userHandle, uid);
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Could not find Profile Owner UID");
+            mProfileOwnerUids.put(userHandle, -1);
+            throw new IllegalArgumentException("Could not find profile owner package "
+                    + packageName);
+        }
+    }
+
+    @Override
+    public void setUserRestrictions(Bundle restrictions, int userHandle) throws RemoteException {
+        checkSystemUid("setUserRestrictions");
+        boolean[] opRestrictions = mOpRestrictions.get(userHandle);
+        if (opRestrictions == null) {
+            opRestrictions = new boolean[AppOpsManager._NUM_OP];
+            mOpRestrictions.put(userHandle, opRestrictions);
+        }
+        for (int i = 0; i < opRestrictions.length; ++i) {
+            String restriction = AppOpsManager.opToRestriction(i);
+            if (restriction != null) {
+                opRestrictions[i] = restrictions.getBoolean(restriction, false);
+            } else {
+                opRestrictions[i] = false;
+            }
+        }
+    }
+
+    @Override
+    public void removeUser(int userHandle) throws RemoteException {
+        checkSystemUid("removeUser");
+        mOpRestrictions.remove(userHandle);
+        mProfileOwnerUids.removeAt(mProfileOwnerUids.indexOfKey(userHandle));
+    }
+
+    private void checkSystemUid(String function) {
+        int uid = Binder.getCallingUid();
+        if (uid != Process.SYSTEM_UID) {
+            throw new SecurityException(function + " must by called by the system");
+        }
+    }
+
 }
index 60212bf..d843b6b 100644 (file)
@@ -40,6 +40,7 @@ import android.os.Handler;
 import android.os.IUserManager;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.AtomicFile;
@@ -50,6 +51,7 @@ import android.util.SparseBooleanArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 
+import com.android.internal.app.IAppOpsService;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
@@ -162,6 +164,8 @@ public class UserManagerService extends IUserManager.Stub {
     private int mNextSerialNumber;
     private int mUserVersion = 0;
 
+    private IAppOpsService mAppOpsService;
+
     private static UserManagerService sInstance;
 
     public static UserManagerService getInstance() {
@@ -236,6 +240,15 @@ public class UserManagerService extends IUserManager.Stub {
     void systemReady() {
         mUserPackageMonitor.register(mContext, null, UserHandle.ALL, false);
         userForeground(UserHandle.USER_OWNER);
+        mAppOpsService = IAppOpsService.Stub.asInterface(
+                ServiceManager.getService(Context.APP_OPS_SERVICE));
+        for (int i = 0; i < mUserIds.length; ++i) {
+            try {
+                mAppOpsService.setUserRestrictions(mUserRestrictions.get(mUserIds[i]), mUserIds[i]);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions");
+            }
+        }
     }
 
     @Override
@@ -482,6 +495,14 @@ public class UserManagerService extends IUserManager.Stub {
         synchronized (mPackagesLock) {
             mUserRestrictions.get(userId).clear();
             mUserRestrictions.get(userId).putAll(restrictions);
+            long token = Binder.clearCallingIdentity();
+            try {
+                mAppOpsService.setUserRestrictions(mUserRestrictions.get(userId), userId);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions");
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
             writeUserLocked(mUsers.get(userId));
         }
     }
@@ -1116,6 +1137,11 @@ public class UserManagerService extends IUserManager.Stub {
                 return false;
             }
             mRemovingUserIds.put(userHandle, true);
+            try {
+                mAppOpsService.removeUser(userHandle);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e);
+            }
             // Set this to a partially created user, so that the user will be purged
             // on next startup, in case the runtime stops now before stopping and
             // removing the user completely.
index 1647425..674c6f4 100644 (file)
@@ -39,6 +39,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.HashMap;
+import java.util.Set;
 
 /**
  * Stores and restores state for the Device and Profile owners. By definition there can be
@@ -137,6 +138,10 @@ public class DeviceOwner {
         return profileOwner != null ? profileOwner.name : null;
     }
 
+    Set<Integer> getProfileOwnerKeys() {
+        return mProfileOwners.keySet();
+    }
+
     boolean hasDeviceOwner() {
         return mDeviceOwner != null;
     }
index 1980d1e..a0c59cc 100644 (file)
@@ -19,6 +19,7 @@ package com.android.server.devicepolicy;
 import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
 
 import com.android.internal.R;
+import com.android.internal.app.IAppOpsService;
 import com.android.internal.os.storage.ExternalStorageFormatter;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
@@ -237,6 +238,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
         }
     };
 
+    private IAppOpsService mAppOpsService;
+
     static class ActiveAdmin {
         private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
         private static final String TAG_DISABLE_CAMERA = "disable-camera";
@@ -1209,6 +1212,22 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
             loadSettingsLocked(getUserData(UserHandle.USER_OWNER), UserHandle.USER_OWNER);
             loadDeviceOwner();
         }
+        mAppOpsService = IAppOpsService.Stub.asInterface(
+                ServiceManager.getService(Context.APP_OPS_SERVICE));
+        if (mDeviceOwner.hasDeviceOwner()) {
+            try {
+                mAppOpsService.setDeviceOwner(mDeviceOwner.getDeviceOwnerPackageName());
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, "Unable to notify AppOpsService of DeviceOwner", e);
+            }
+        }
+        for (Integer i : mDeviceOwner.getProfileOwnerKeys()) {
+            try {
+                mAppOpsService.setProfileOwner(mDeviceOwner.getProfileOwnerName(i), i);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, "Unable to notify AppOpsService of ProfileOwner", e);
+            }
+        }
     }
 
     private void handlePasswordExpirationNotification(int userHandle) {
@@ -2953,6 +2972,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                         "Trying to set device owner but device owner is already set.");
             }
 
+            long token = Binder.clearCallingIdentity();
+            try {
+                mAppOpsService.setDeviceOwner(packageName);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, "Unable to notify AppOpsService of DeviceOwner", e);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
             if (mDeviceOwner == null) {
                 // Device owner is not set and does not exist, set it.
                 mDeviceOwner = DeviceOwner.createWithDeviceOwner(packageName, ownerName);
@@ -3029,6 +3056,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                 throw new IllegalStateException(
                         "Trying to set profile owner but user is already set-up.");
             }
+            long token = Binder.clearCallingIdentity();
+            try {
+                mAppOpsService.setProfileOwner(packageName, userHandle);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, "Unable to notify AppOpsService of ProfileOwner", e);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
             if (mDeviceOwner == null) {
                 // Device owner state does not exist, create it.
                 mDeviceOwner = DeviceOwner.createWithProfileOwner(packageName, ownerName,