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

Commit 9b4a2858 authored by Daniel Sandler's avatar Daniel Sandler Committed by Android (Google) Code Review
Browse files

Merge "Migrate package notification restrictions to AppOps."

parents b06870bf 4a900acd
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ public class AppOpsManager {
    public static final int OP_READ_CALENDAR = 8;
    public static final int OP_WRITE_CALENDAR = 9;
    public static final int OP_WIFI_SCAN = 10;
    public static final int OP_POST_NOTIFICATION = 11;

    private static String[] sOpNames = new String[] {
        "COARSE_LOCATION",
@@ -60,6 +61,7 @@ public class AppOpsManager {
        "READ_CALENDAR",
        "WRITE_CALENDAR",
        "WIFI_SCAN",
        "POST_NOTIFICATION",
    };

    private static String[] sOpPerms = new String[] {
@@ -74,6 +76,7 @@ public class AppOpsManager {
        android.Manifest.permission.READ_CALENDAR,
        android.Manifest.permission.WRITE_CALENDAR,
        android.Manifest.permission.ACCESS_WIFI_STATE,
        null, // no permission required for notifications
    };

    public static String opToName(int op) {
+2 −2
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@ interface INotificationManager
            in Notification notification, inout int[] idReceived, int userId);
    void cancelNotificationWithTag(String pkg, String tag, int id, int userId);

    void setNotificationsEnabledForPackage(String pkg, boolean enabled);
    boolean areNotificationsEnabledForPackage(String pkg);
    void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled);
    boolean areNotificationsEnabledForPackage(String pkg, int uid);
}
+59 −75
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.ITransientNotification;
@@ -35,6 +36,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
@@ -62,21 +64,17 @@ import android.util.Slog;
import android.util.Xml;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.RemoteViews;
import android.widget.Toast;

import com.android.internal.statusbar.StatusBarNotification;
import com.android.internal.util.FastXmlSerializer;

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

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -156,6 +154,8 @@ public class NotificationManagerService extends INotificationManager.Stub
    private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
    private NotificationRecord mLedNotification;

    private final AppOpsManager mAppOps;

    // Notification control database. For now just contains disabled packages.
    private AtomicFile mPolicyFile;
    private HashSet<String> mBlockedPackages = new HashSet<String>();
@@ -218,79 +218,35 @@ public class NotificationManagerService extends INotificationManager.Stub
        }
    }

    private void writeBlockDb() {
        synchronized(mBlockedPackages) {
            FileOutputStream outfile = null;
            try {
                outfile = mPolicyFile.startWrite();

                XmlSerializer out = new FastXmlSerializer();
                out.setOutput(outfile, "utf-8");

                out.startDocument(null, true);

                out.startTag(null, TAG_BODY); {
                    out.attribute(null, ATTR_VERSION, String.valueOf(DB_VERSION));
                    out.startTag(null, TAG_BLOCKED_PKGS); {
                        // write all known network policies
                        for (String pkg : mBlockedPackages) {
                            out.startTag(null, TAG_PACKAGE); {
                                out.attribute(null, ATTR_NAME, pkg);
                            } out.endTag(null, TAG_PACKAGE);
                        }
                    } out.endTag(null, TAG_BLOCKED_PKGS);
                } out.endTag(null, TAG_BODY);

                out.endDocument();

                mPolicyFile.finishWrite(outfile);
            } catch (IOException e) {
                if (outfile != null) {
                    mPolicyFile.failWrite(outfile);
                }
            }
        }
    }

    public boolean areNotificationsEnabledForPackage(String pkg) {
    /**
     * Use this when you just want to know if notifications are OK for this package.
     */
    public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
        checkCallerIsSystem();
        return areNotificationsEnabledForPackageInt(pkg);
        return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
                == AppOpsManager.MODE_ALLOWED);
    }

    // Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
    private boolean areNotificationsEnabledForPackageInt(String pkg) {
        final boolean enabled = !mBlockedPackages.contains(pkg);
        if (DBG) {
            Slog.v(TAG, "notifications are " + (enabled?"en":"dis") + "abled for " + pkg);
    /** Use this when you actually want to post a notification or toast.
     *
     * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
     */
    private boolean noteNotificationOp(String pkg, int uid) {
        if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
                != AppOpsManager.MODE_ALLOWED) {
            Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
            return false;
        }
        return enabled;
        return true;
    }

    public void setNotificationsEnabledForPackage(String pkg, boolean enabled) {
    public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
        checkCallerIsSystem();
        if (DBG) {
        if (true||DBG) {
            Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
        }
        if (enabled) {
            mBlockedPackages.remove(pkg);
        } else {
            mBlockedPackages.add(pkg);

            // Now, cancel any outstanding notifications that are part of a just-disabled app
            if (ENABLE_BLOCKED_NOTIFICATIONS) {
                synchronized (mNotificationList) {
                    final int N = mNotificationList.size();
                    for (int i=0; i<N; i++) {
                        final NotificationRecord r = mNotificationList.get(i);
                        if (r.pkg.equals(pkg)) {
                            cancelNotificationLocked(r, false);
                        }
                    }
                }
            }
            // Don't bother canceling toasts, they'll go away soon enough.
        }
        writeBlockDb();
        mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
                enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
    }


@@ -628,7 +584,9 @@ public class NotificationManagerService extends INotificationManager.Stub
        mToastQueue = new ArrayList<ToastRecord>();
        mHandler = new WorkerHandler();

        loadBlockDb();
        mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);

        importOldBlockDb();

        mStatusBar = statusBar;
        statusBar.setNotificationCallbacks(mNotificationCallbacks);
@@ -685,6 +643,28 @@ public class NotificationManagerService extends INotificationManager.Stub
        observer.observe();
    }

    /**
     * Read the old XML-based app block database and import those blockages into the AppOps system.
     */
    private void importOldBlockDb() {
        loadBlockDb();

        PackageManager pm = mContext.getPackageManager();
        for (String pkg : mBlockedPackages) {
            PackageInfo info = null;
            try {
                info = pm.getPackageInfo(pkg, 0);
                setNotificationsEnabledForPackage(pkg, info.applicationInfo.uid, false);
            } catch (NameNotFoundException e) {
                // forget you
            }
        }
        mBlockedPackages.clear();
        if (mPolicyFile != null) {
            mPolicyFile.delete();
        }
    }

    void systemReady() {
        mAudioService = IAudioService.Stub.asInterface(
                ServiceManager.getService(Context.AUDIO_SERVICE));
@@ -706,10 +686,12 @@ public class NotificationManagerService extends INotificationManager.Stub

        final boolean isSystemToast = ("android".equals(pkg));

        if (ENABLE_BLOCKED_TOASTS && !isSystemToast && !areNotificationsEnabledForPackageInt(pkg)) {
        if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
            if (!isSystemToast) {
                Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
                return;
            }
        }

        synchronized (mToastQueue) {
            int callingPid = Binder.getCallingPid();
@@ -982,10 +964,12 @@ public class NotificationManagerService extends INotificationManager.Stub
        // 3. Apply local rules

        // blocked apps
        if (ENABLE_BLOCKED_NOTIFICATIONS && !isSystemNotification && !areNotificationsEnabledForPackageInt(pkg)) {
        if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
            if (!isSystemNotification) {
                score = JUNK_SCORE;
                Slog.e(TAG, "Suppressing notification from package " + pkg + " by user request.");
            }
        }

        if (DBG) {
            Slog.v(TAG, "Assigned score=" + score + " to " + notification);