Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8b10cf24 authored by Shashank Mittal's avatar Shashank Mittal Committed by Roman Birg
Browse files

AppOps: Add support for AppOps policy file

Add support to read AppOps policy file.
AppOps policy file can be used to configure Ops default policy.

Change-Id: Idb4e3501ff9f58e491b06e74152342aa9574afbf
parent 646827a4
Loading
Loading
Loading
Loading
+115 −1
Original line number Diff line number Diff line
@@ -116,7 +116,8 @@ public class AppOpsManager {

    // when adding one of these:
    //  - increment _NUM_OP
    //  - add rows to sOpToSwitch, sOpToString, sOpNames, sOpPerms, sOpDefaultMode, sOpDefaultStrictMode
    //  - add rows to sOpToSwitch, sOpToString, sOpNames, sOpPerms, sOpDefaultMode, sOpDefaultStrictMode,
    //    sOpToOpString, sOpStrictMode.
    //  - add descriptive strings to frameworks/base/core/res/res/values/config.xml
    //  - add descriptive strings to Settings/res/values/arrays.xml
    //  - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app)
@@ -913,6 +914,75 @@ public class AppOpsManager {
            AppOpsManager.MODE_ALLOWED, // OP_GET_ACCOUNTS
    };

    /**
     * This specifies if operation is in strict mode.
     */
    private final static boolean[] sOpStrictMode = new boolean[] {
        true,     // OP_COARSE_LOCATION
        true,     // OP_FINE_LOCATION
        true,     // OP_GPS
        false,    // OP_VIBRATE
        true,     // OP_READ_CONTACTS
        true,     // OP_WRITE_CONTACTS
        true,     // OP_READ_CALL_LOG
        true,     // OP_WRITE_CALL_LOG
        false,    // OP_READ_CALENDAR
        false,    // OP_WRITE_CALENDAR
        true,     // OP_WIFI_SCAN
        false,    // OP_POST_NOTIFICATION
        false,    // OP_NEIGHBORING_CELLS
        true,     // OP_CALL_PHONE
        true,     // OP_READ_SMS
        true,     // OP_WRITE_SMS
        false,    // OP_RECEIVE_SMS
        false,    // OP_RECEIVE_EMERGECY_SMS
        true,     // OP_RECEIVE_MMS
        false,    // OP_RECEIVE_WAP_PUSH
        true,     // OP_SEND_SMS
        false,    // OP_READ_ICC_SMS
        false,    // OP_WRITE_ICC_SMS
        false,    // OP_WRITE_SETTINGS
        false,    // OP_SYSTEM_ALERT_WINDOW
        false,    // OP_ACCESS_NOTIFICATIONS
        true,     // OP_CAMERA
        true,     // OP_RECORD_AUDIO
        false,    // OP_PLAY_AUDIO
        false,    // OP_READ_CLIPBOARD
        false,    // OP_WRITE_CLIPBOARD
        false,    // OP_TAKE_MEDIA_BUTTONS
        false,    // OP_TAKE_AUDIO_FOCUS
        false,    // OP_AUDIO_MASTER_VOLUME
        false,    // OP_AUDIO_VOICE_VOLUME
        false,    // OP_AUDIO_RING_VOLUME
        false,    // OP_AUDIO_MEDIA_VOLUME
        false,    // OP_AUDIO_ALARM_VOLUME
        false,    // OP_AUDIO_NOTIFICATION_VOLUME
        false,    // OP_AUDIO_BLUETOOTH_VOLUME
        false,    // OP_WAKE_LOCK
        false,    // OP_MONITOR_LOCATION
        true,     // OP_MONITOR_HIGH_POWER_LOCATION
        false,    // OP_GET_USAGE_STATS
        false,    // OP_MUTE_MICROPHONE
        false,    // OP_TOAST_WINDOW
        false,    // OP_PROJECT_MEDIA
        false,    // OP_ACTIVATE_VPN
        true,     // OP WALLPAPER
        false,    //ASSIST_STRUCTURE
        false,    //ASSIST_SCREENSHOT
        false,    //READ_PHONE_STATE
        false,    //ADD_VOICEMAIL
        false,    // USE_SIP
        false,    // PROCESS_OUTGOING_CALLS
        false,    // USE_FINGERPRINT
        false,    // BODY_SENSORS
        false,    // READ_CELL_BROADCASTS
        false,    // MOCK_LOCATION
        true,     // READ_EXTERNAL_STORAGE
        true,     // WRITE_EXTERNAL_STORAGE
        false,    // TURN_ON_SCREEN
        false,    // GET_ACCOUNTS
    };

    /**
     * This specifies whether each option is allowed to be reset
     * when resetting all app preferences.  Disable reset for
@@ -1035,6 +1105,10 @@ public class AppOpsManager {
            throw new IllegalStateException("sOpAllowSYstemRestrictionsBypass length "
                    + sOpRestrictions.length + " should be " + _NUM_OP);
        }
        if (sOpStrictMode.length != _NUM_OP) {
            throw new IllegalStateException("sOpStrictMode length "
                    + sOpStrictMode.length + " should be " + _NUM_OP);
        }
        for (int i=0; i<_NUM_OP; i++) {
            if (sOpToString[i] != null) {
                sOpStrToOp.put(sOpToString[i], i);
@@ -1860,4 +1934,44 @@ public class AppOpsManager {
    public static boolean isStrictEnable() {
        return SystemProperties.getBoolean("persist.sys.strict_op_enable", false);
    }

    /**
     * Check if op in strict mode
     * @hide
     */
    public static boolean isStrictOp(int code) {
        return sOpStrictMode[code];
    }


    /** @hide */
    public static int stringToMode(String permission) {
        if ("allowed".equalsIgnoreCase(permission)) {
            return AppOpsManager.MODE_ALLOWED;
        } else if ("ignored".equalsIgnoreCase(permission)) {
            return AppOpsManager.MODE_IGNORED;
        } else if ("ask".equalsIgnoreCase(permission)) {
            return AppOpsManager.MODE_ASK;
        }
        return AppOpsManager.MODE_ERRORED;
    }

    /** @hide */
    public static int stringOpToOp (String op) {
        Integer val = sOpStrToOp.get(op);
        if (val == null) {
            val = OP_NONE;
        }
        return val;
    }

    /** @hide */
    public boolean isControlAllowed(int op, String packageName) {
        boolean isShow = true;
        try {
            isShow = mService.isControlAllowed(op, packageName);
        } catch (RemoteException e) {
        }
        return isShow;
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -46,4 +46,5 @@ interface IAppOpsService {

    void setUserRestrictions(in Bundle restrictions, int userHandle);
    void removeUser(int userHandle);
    boolean isControlAllowed(int code, String packageName);
}
+441 −0
Original line number Diff line number Diff line
/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.android.server;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;

import com.android.internal.util.XmlUtils;

public class AppOpsPolicy {
    static final String TAG = "AppOpsPolicy";
    static final boolean DEBUG = false;
    final File mFile;
    final Context mContext;
    public static final int CONTROL_SHOW = 0;

    public static final int CONTROL_NOSHOW = 1;

    public static final int CONTROL_UNKNOWN = 2;

    public static int stringToControl(String show) {
        if ("true".equalsIgnoreCase(show)) {
            return CONTROL_SHOW;
        } else if ("false".equalsIgnoreCase(show)) {
            return CONTROL_NOSHOW;
        }
        return CONTROL_UNKNOWN;
    }

    HashMap<String, PolicyPkg> mPolicy = new HashMap<String, PolicyPkg>();

    public AppOpsPolicy(File file, Context context) {
        super();
        mFile = file;
        mContext = context;
    }

    public final static class PolicyPkg extends SparseArray<PolicyOp> {
        public String packageName;
        public int mode;
        public int show;
        public String type;

        public PolicyPkg(String packageName, int mode, int show, String type) {
            this.packageName = packageName;
            this.mode = mode;
            this.show = show;
            this.type = type;
        }

        @Override
        public String toString() {
            return "PolicyPkg [packageName=" + packageName + ", mode=" + mode
                    + ", show=" + show + ", type=" + type + "]";
        }

    }

    public final static class PolicyOp {
        public int op;
        public int mode;
        public int show;

        public PolicyOp(int op, int mode, int show) {
            this.op = op;
            this.mode = mode;
            this.show = show;
        }

        @Override
        public String toString() {
            return "PolicyOp [op=" + op + ", mode=" + mode + ", show=" + show
                    + "]";
        }
    }

    void readPolicy() {
        FileInputStream stream;
        synchronized (mFile) {
            try {
                stream = new FileInputStream(mFile);
            } catch (FileNotFoundException e) {
                Slog.i(TAG, "App ops policy file (" + mFile.getPath()
                        + ") not found; Skipping.");
                return;
            }
            boolean success = false;
            try {
                XmlPullParser parser = Xml.newPullParser();
                parser.setInput(stream, null);
                int type;
                success = true;
                while ((type = parser.next()) != XmlPullParser.START_TAG
                        && type != XmlPullParser.END_DOCUMENT) {
                    ;
                }
                if (type != XmlPullParser.START_TAG) {
                    throw new IllegalStateException("no start tag found");
                }

                int outerDepth = parser.getDepth();
                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("user-app")
                            || tagName.equals("system-app")) {
                        readDefaultPolicy(parser, tagName);
                    } else if (tagName.equals("application")) {
                        readApplicationPolicy(parser);
                    } else {
                        Slog.w(TAG, "Unknown element under <appops-policy>: "
                                + parser.getName());
                        XmlUtils.skipCurrentTag(parser);
                    }
                }
            } catch (IllegalStateException e) {
                Slog.w(TAG, "Failed parsing " + e);
            } catch (NullPointerException e) {
                Slog.w(TAG, "Failed parsing " + e);
            } catch (NumberFormatException e) {
                Slog.w(TAG, "Failed parsing " + e);
            } catch (XmlPullParserException e) {
                Slog.w(TAG, "Failed parsing " + e);
            } catch (IOException e) {
                Slog.w(TAG, "Failed parsing " + e);
            } catch (IndexOutOfBoundsException e) {
                Slog.w(TAG, "Failed parsing " + e);
            } finally {
                if (!success) {
                    mPolicy.clear();
                }
                try {
                    stream.close();
                } catch (IOException e) {
                }
            }
        }
    }

    private void readDefaultPolicy(XmlPullParser parser, String packageName)
            throws NumberFormatException, XmlPullParserException, IOException {
        if (!"user-app".equalsIgnoreCase(packageName)
                && !"system-app".equalsIgnoreCase(packageName)) {
            return;
        }
        int mode = AppOpsManager.stringToMode(parser.getAttributeValue(null,
                "permission"));
        int show = stringToControl(parser.getAttributeValue(null, "show"));
        if (mode == AppOpsManager.MODE_ERRORED && show == CONTROL_UNKNOWN) {
            return;
        }
        PolicyPkg pkg = this.mPolicy.get(packageName);
        if (pkg == null) {
            pkg = new PolicyPkg(packageName, mode, show, packageName);
            this.mPolicy.put(packageName, pkg);
        } else {
            Slog.w(TAG, "Duplicate policy found for package: " + packageName
                    + " of type: " + packageName);
            pkg.mode = mode;
            pkg.show = show;
        }
    }

    private void readApplicationPolicy(XmlPullParser parser)
            throws NumberFormatException, XmlPullParserException, IOException {
        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("pkg")) {
                readPkgPolicy(parser);
            } else {
                Slog.w(TAG,
                        "Unknown element under <application>: "
                                + parser.getName());
                XmlUtils.skipCurrentTag(parser);
            }
        }
    }

    private void readPkgPolicy(XmlPullParser parser)
            throws NumberFormatException, XmlPullParserException, IOException {
        String packageName = parser.getAttributeValue(null, "name");
        if (packageName == null)
            return;
        String appType = parser.getAttributeValue(null, "type");
        if (appType == null)
            return;
        int mode = AppOpsManager.stringToMode(parser.getAttributeValue(null,
                "permission"));
        int show = stringToControl(parser.getAttributeValue(null, "show"));
        String key = packageName + "." + appType;
        PolicyPkg pkg = this.mPolicy.get(key);
        if (pkg == null) {
            pkg = new PolicyPkg(packageName, mode, show, appType);
            this.mPolicy.put(key, pkg);
        } else {
            Slog.w(TAG, "Duplicate policy found for package: " + packageName
                    + " of type: " + appType);
            pkg.mode = mode;
            pkg.show = show;
        }

        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")) {
                readOpPolicy(parser, pkg);
            } else {
                Slog.w(TAG, "Unknown element under <pkg>: " + parser.getName());
                XmlUtils.skipCurrentTag(parser);
            }
        }
    }

    private void readOpPolicy(XmlPullParser parser, PolicyPkg pkg)
            throws NumberFormatException, XmlPullParserException, IOException {
        if (pkg == null) {
            return;
        }
        String opName = parser.getAttributeValue(null, "name");
        if (opName == null) {
            Slog.w(TAG, "Op name is null");
            return;
        }
        int code = AppOpsManager.stringOpToOp(opName);
        if (code == AppOpsManager.OP_NONE) {
            Slog.w(TAG, "Unknown Op: " + opName);
            return;
        }
        int mode = AppOpsManager.stringToMode(parser.getAttributeValue(null,
                "permission"));
        int show = stringToControl(parser.getAttributeValue(null, "show"));
        if (mode == AppOpsManager.MODE_ERRORED && show == CONTROL_UNKNOWN) {
            return;
        }
        PolicyOp op = pkg.get(code);
        if (op == null) {
            op = new PolicyOp(code, mode, show);
            pkg.put(code, op);
        } else {
            Slog.w(TAG, "Duplicate policy found for package: "
                    + pkg.packageName + " type: " + pkg.type + " op: " + op.op);
            op.mode = mode;
            op.show = show;
        }
    }

    void debugPoilcy() {
        Iterator<Map.Entry<String, PolicyPkg>> iterator = mPolicy.entrySet()
                .iterator();
        while (iterator.hasNext()) {
            String key = iterator.next().getKey();
            if (DEBUG)
                Slog.d(TAG, "Key: " + key);
            PolicyPkg pkg = mPolicy.get(key);
            if (pkg == null) {
                if (DEBUG)
                    Slog.d(TAG, "Pkg is null for key: " + key);
                continue;
            }
            if (DEBUG)
                Slog.d(TAG, pkg.toString());
            for (int i = 0; i < pkg.size(); i++) {
                PolicyOp op = pkg.valueAt(i);
                if (DEBUG)
                    Slog.d(TAG, op.toString());
            }
        }
    }

    private String getAppType(String packageName) {
        String appType = null;
        ApplicationInfo appInfo = null;
        if (mContext != null) {
            try {
                appInfo = mContext.getPackageManager().getApplicationInfo(
                        packageName, 0);
            } catch (NameNotFoundException e) {
                appInfo = null;
            }
            if (appInfo != null) {
                if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
                    appType = "system-app";
                } else {
                    appType = "user-app";
                }
            }
        } else {
            Slog.e(TAG, "Context is null");
        }
        return appType;
    }

    public boolean isControlAllowed(int code, String packageName) {
        boolean isShow = true;
        int show = CONTROL_UNKNOWN;
        PolicyPkg pkg;
        String key;
        String type;

        if (mPolicy == null) {
            return isShow;
        }

        type = getAppType(packageName);
        if (type != null) {
            key = type;
            pkg = mPolicy.get(key);
            if (pkg != null && pkg.show != CONTROL_UNKNOWN) {
                show = pkg.show;
            }
        }
        key = packageName;
        if (type != null) {
            key = key + "." + type;
        }
        pkg = mPolicy.get(key);
        if (pkg != null) {
            if (pkg.show != CONTROL_UNKNOWN) {
                show = pkg.show;
            }
            PolicyOp op = pkg.get(code);
            if (op != null) {
                if (op.show != CONTROL_UNKNOWN) {
                    show = op.show;
                }
            }
        }
        if (show == CONTROL_NOSHOW) {
            isShow = false;
        }
        return isShow;
    }

    public int getDefualtMode(int code, String packageName) {
        int mode = AppOpsManager.MODE_ERRORED;
        PolicyPkg pkg;
        String key;
        String type;

        if (mPolicy == null) {
            return mode;
        }
        if (DEBUG)
            Slog.d(TAG, "Default mode requested for op=" + code + " package="
                    + packageName);
        type = getAppType(packageName);
        if (type != null) {
            // Get value based on 'type'
            key = type;
            pkg = mPolicy.get(key);
            if (pkg != null && pkg.mode != AppOpsManager.MODE_ERRORED) {
                if (DEBUG)
                    Slog.d(TAG, "Setting value based on type: " + pkg);
                mode = pkg.mode;
            }
        }
        // Get value based on 'pkg'.
        key = packageName;
        if (type != null) {
            key = key + "." + type;
        }
        pkg = mPolicy.get(key);
        if (pkg != null) {
            if (pkg.mode != AppOpsManager.MODE_ERRORED) {
                if (DEBUG)
                    Slog.d(TAG, "Setting value based on packageName: " + pkg);
                mode = pkg.mode;
            }
            // Get value base on 'op'
            PolicyOp op = pkg.get(code);
            if (op != null) {
                if (op.mode != AppOpsManager.MODE_ERRORED) {
                    if (DEBUG)
                        Slog.d(TAG, "Setting value based on op: " + op);
                    mode = op.mode;
                }
            }
        }
        if (DEBUG)
            Slog.d(TAG, "Returning mode=" + mode);
        return mode;
    }
}
 No newline at end of file
+48 −7

File changed.

Preview size limit exceeded, changes collapsed.