Commits

Robert Craig committed 3f19817

New AppOps controls.

New code allows policy to lock certain
operations that have already been granted
to apps on install. This will provide an
enterprise like approach that can work
in conjunction with the install-time
mac policy already in place.

Comments (0)

Files changed (2)

core/java/android/app/AppOpsManager.java

             null, // no permission for writing clipboard
     };
 
+    private static HashMap<String, Integer> nameToOp = new HashMap<String, Integer>();
+    static {
+        for (int i = 0; i < _NUM_OP; i++) {
+            nameToOp.put(sOpNames[i], sOpToSwitch[i]);
+        }
+    }
+
+    /**
+     * Retrieve the op switch for the given operation name.
+     */
+    public static int nameToOp(String name) {
+        int ret = OP_NONE;
+        if (nameToOp.containsKey(name)) {
+            ret = nameToOp.get(name);
+        }
+        return ret;
+    }
+
     /**
      * Retrieve the op switch that controls the given operation.
      */
         private final long mTime;
         private final long mRejectTime;
         private final int mDuration;
+        private final boolean mLocked;
 
-        public OpEntry(int op, int mode, long time, long rejectTime, int duration) {
+        public OpEntry(int op, int mode, long time, long rejectTime, int duration, boolean locked) {
             mOp = op;
             mMode = mode;
             mTime = time;
             mRejectTime = rejectTime;
             mDuration = duration;
+            mLocked = locked;
         }
 
         public int getOp() {
             return mDuration == -1 ? (int)(System.currentTimeMillis()-mTime) : mDuration;
         }
 
+        public boolean getLocked() {
+            return mLocked;
+        }
+
         @Override
         public int describeContents() {
             return 0;
             dest.writeLong(mTime);
             dest.writeLong(mRejectTime);
             dest.writeInt(mDuration);
+            dest.writeInt(mLocked ? 1 : 0);
         }
 
         OpEntry(Parcel source) {
             mTime = source.readLong();
             mRejectTime = source.readLong();
             mDuration = source.readInt();
+            mLocked = (source.readInt() == 1) ? true : false;
         }
 
         public static final Creator<OpEntry> CREATOR = new Creator<OpEntry>() {

services/java/com/android/server/AppOpsService.java

 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.AsyncTask;
 import android.os.Binder;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Process;
     final SparseArray<HashMap<String, Ops>> mUidOps
             = new SparseArray<HashMap<String, Ops>>();
 
+    final HashMap<String, SparseArray<Op>> mLockedPolicy
+            = new HashMap<String, SparseArray<Op>>();
+
+    static final File[] EOPS_POLICY_FILE = {
+        new File(Environment.getDataDirectory(), "security/eops.xml"),
+        new File(Environment.getRootDirectory(), "etc/security/eops.xml")
+    };
+
+
     public final static class Ops extends SparseArray<Op> {
         public final String packageName;
         public final int uid;
         public long time;
         public long rejectTime;
         public int nesting;
+        public boolean locked;
 
         public Op(int _op) {
+            this(_op, false);
+        }
+
+        public Op(int _op, boolean _locked) {
             op = _op;
             mode = AppOpsManager.MODE_ALLOWED;
+            locked = _locked;
         }
     }
 
         }
     }
 
+    private void readEnterprisePolicy() {
+        for (int i = 0; i < EOPS_POLICY_FILE.length; i++) {
+            File policy = EOPS_POLICY_FILE[i];
+            if (policy.canRead()) {
+                Slog.d(TAG, "Using eops policy " + policy.getPath());
+                readState(new AtomicFile(policy));
+                break;
+            }
+            Slog.d(TAG, "Ignoring eops policy " + policy.getPath());
+        }
+    }
+
     public AppOpsService(File storagePath) {
         mFile = new AtomicFile(storagePath);
         mHandler = new Handler();
-        readState();
+        readState(mFile);
+        readEnterprisePolicy();
     }
-    
+
     public void publish(Context context) {
         mContext = context;
         ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
     }
 
+
     public void systemReady() {
         synchronized (this) {
             boolean changed = false;
             for (int j=0; j<pkgOps.size(); j++) {
                 Op curOp = pkgOps.valueAt(j);
                 resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
-                        curOp.rejectTime, curOp.duration));
+                        curOp.rejectTime, curOp.duration, curOp.locked));
             }
         } else {
             for (int j=0; j<ops.length; j++) {
                         resOps = new ArrayList<AppOpsManager.OpEntry>();
                     }
                     resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
-                            curOp.rejectTime, curOp.duration));
+                            curOp.rejectTime, curOp.duration, curOp.locked));
                 }
             }
         }
             for (int i=0; i<mUidOps.size(); i++) {
                 HashMap<String, Ops> packages = mUidOps.valueAt(i);
                 for (Ops pkgOps : packages.values()) {
+                    pkgOps = (unionPolicies(pkgOps.uid, pkgOps.packageName)).get(pkgOps.packageName);
                     ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
                     if (resOps != null) {
                         if (res == null) {
         synchronized (this) {
             Op op = getOpLocked(code, uid, packageName, true);
             if (op != null) {
-                if (op.mode != mode) {
+                if (op.mode != mode && !op.locked) {
                     op.mode = mode;
                     ArrayList<Callback> cbs = mOpModeWatchers.get(code);
                     if (cbs != null) {
         throw new IllegalArgumentException("Bad operation #" + op);
     }
 
-    private Ops getOpsLocked(int uid, String packageName, boolean edit) {
+    /* Overlay enterprise policy on user policy where appropriate */
+    private HashMap<String, Ops> unionPolicies(int uid, String packageName) {
         HashMap<String, Ops> pkgOps = mUidOps.get(uid);
+        // If no eops policy file then just return here.
+        if (mLockedPolicy.isEmpty()) {
+            return pkgOps;
+        }
+
+        SparseArray<Op> lockedOps = null;
+        try {
+            PackageManager pm = mContext.getPackageManager();
+            String se = (pm.getApplicationInfo(packageName, 0)).seinfo;
+            lockedOps = mLockedPolicy.get(se);
+        } catch (NameNotFoundException e) {
+            return pkgOps;
+        }
+
+        if (lockedOps == null) {
+            return pkgOps;
+        }
+
+        if (pkgOps == null) {
+            pkgOps = new HashMap<String, Ops>(1);
+        }
+
+        Ops ops = pkgOps.get(packageName);
+        if (ops == null) {
+            ops = new Ops(packageName, uid);
+            pkgOps.put(packageName, ops);
+        }
+
+        for (int i = 0; i < lockedOps.size(); i++) {
+            Op o = lockedOps.valueAt(i);
+            ops.put(o.op, o);
+        }
+
+        return pkgOps;
+    }
+
+    private Ops getOpsLocked(int uid, String packageName, boolean edit) {
+        HashMap<String, Ops> pkgOps = unionPolicies(uid, packageName);
         if (pkgOps == null) {
             if (!edit) {
                 return null;
             if (!edit) {
                 return null;
             }
+
             op = new Op(code);
             ops.put(code, op);
         }
         return op;
     }
 
-    void readState() {
-        synchronized (mFile) {
+    void readState(AtomicFile file) {
+        synchronized (file) {
             synchronized (this) {
                 FileInputStream stream;
                 try {
-                    stream = mFile.openRead();
+                    stream = file.openRead();
                 } catch (FileNotFoundException e) {
-                    Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
+                    Slog.i(TAG, "No existing app ops " + file.getBaseFile() + "; starting empty");
                     return;
                 }
                 boolean success = false;
                         String tagName = parser.getName();
                         if (tagName.equals("pkg")) {
                             readPackage(parser);
+                        } else if (tagName.equals("seinfo")) {
+                            readSeinfo(parser);
                         } else {
                             Slog.w(TAG, "Unknown element under <app-ops>: "
                                     + parser.getName());
         }
     }
 
+    void readSeinfo(XmlPullParser parser) throws XmlPullParserException, IOException {
+        String seinfo = parser.getAttributeValue(null, "name");
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals("op")) {
+                readOp(parser, seinfo);
+            } else {
+                Slog.w(TAG, "Unknown element under <seinfo>: "
+                        + parser.getName());
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+    }
+
+    void readOp(XmlPullParser parser, String seinfo) {
+        String op = parser.getAttributeValue(null, "name");
+        int opInt = AppOpsManager.nameToOp(op);
+        if (seinfo != null && opInt != AppOpsManager.OP_NONE) {
+            SparseArray<Op> eops = mLockedPolicy.get(seinfo);
+            if (eops == null) {
+                eops = new SparseArray<Op>();
+                mLockedPolicy.put(seinfo, eops);
+            }
+            // true means locked operation.
+            Op eop = new Op(opInt, true);
+            // For now everything is ignored.
+            eop.mode = AppOpsManager.MODE_IGNORED;
+            eops.put(opInt, eop);
+        }
+    }
+
     void readPackage(XmlPullParser parser) throws NumberFormatException,
             XmlPullParserException, IOException {
         String pkgName = parser.getAttributeValue(null, "n");