diff options
3 files changed, 166 insertions, 51 deletions
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 3300cdb..2d15d13 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -36,6 +36,7 @@ import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.app.PendingIntent; import android.app.StatusBarManager; +import android.app.backup.BackupManager; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; @@ -113,12 +114,16 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; 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.InputStream; +import java.io.OutputStream; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayDeque; @@ -330,6 +335,37 @@ public class NotificationManagerService extends SystemService { } + private void readPolicyXml(InputStream stream, boolean forRestore) + throws XmlPullParserException, NumberFormatException, IOException { + final XmlPullParser parser = Xml.newPullParser(); + parser.setInput(stream, StandardCharsets.UTF_8.name()); + + int type; + String tag; + int version = DB_VERSION; + while ((type = parser.next()) != END_DOCUMENT) { + tag = parser.getName(); + if (type == START_TAG) { + if (TAG_NOTIFICATION_POLICY.equals(tag)) { + version = Integer.parseInt( + parser.getAttributeValue(null, ATTR_VERSION)); + } else if (TAG_BLOCKED_PKGS.equals(tag)) { + while ((type = parser.next()) != END_DOCUMENT) { + tag = parser.getName(); + if (TAG_PACKAGE.equals(tag)) { + mBlockedPackages.add( + parser.getAttributeValue(null, ATTR_NAME)); + } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) { + break; + } + } + } + } + mZenModeHelper.readXml(parser, forRestore); + mRankingHelper.readXml(parser, forRestore); + } + } + private void loadPolicyFile() { if (DBG) Slog.d(TAG, "loadPolicyFile"); synchronized(mPolicyFile) { @@ -338,33 +374,7 @@ public class NotificationManagerService extends SystemService { FileInputStream infile = null; try { infile = mPolicyFile.openRead(); - final XmlPullParser parser = Xml.newPullParser(); - parser.setInput(infile, StandardCharsets.UTF_8.name()); - - int type; - String tag; - int version = DB_VERSION; - while ((type = parser.next()) != END_DOCUMENT) { - tag = parser.getName(); - if (type == START_TAG) { - if (TAG_NOTIFICATION_POLICY.equals(tag)) { - version = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VERSION)); - } else if (TAG_BLOCKED_PKGS.equals(tag)) { - while ((type = parser.next()) != END_DOCUMENT) { - tag = parser.getName(); - if (TAG_PACKAGE.equals(tag)) { - mBlockedPackages.add( - parser.getAttributeValue(null, ATTR_NAME)); - } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) { - break; - } - } - } - } - mZenModeHelper.readXml(parser); - mRankingHelper.readXml(parser); - } + readPolicyXml(infile, false /*forRestore*/); } catch (FileNotFoundException e) { // No data yet } catch (IOException e) { @@ -396,21 +406,26 @@ public class NotificationManagerService extends SystemService { } try { - final XmlSerializer out = new FastXmlSerializer(); - out.setOutput(stream, StandardCharsets.UTF_8.name()); - out.startDocument(null, true); - out.startTag(null, TAG_NOTIFICATION_POLICY); - out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); - mZenModeHelper.writeXml(out); - mRankingHelper.writeXml(out); - out.endTag(null, TAG_NOTIFICATION_POLICY); - out.endDocument(); + writePolicyXml(stream, false /*forBackup*/); mPolicyFile.finishWrite(stream); } catch (IOException e) { Slog.w(TAG, "Failed to save policy file, restoring backup", e); mPolicyFile.failWrite(stream); } } + BackupManager.dataChanged(getContext().getPackageName()); + } + + private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException { + final XmlSerializer out = new FastXmlSerializer(); + out.setOutput(stream, StandardCharsets.UTF_8.name()); + out.startDocument(null, true); + out.startTag(null, TAG_NOTIFICATION_POLICY); + out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); + mZenModeHelper.writeXml(out, forBackup); + mRankingHelper.writeXml(out, forBackup); + out.endTag(null, TAG_NOTIFICATION_POLICY); + out.endDocument(); } /** Use this when you actually want to post a notification or toast. @@ -722,6 +737,7 @@ public class NotificationManagerService extends SystemService { } mListeners.onPackagesChanged(queryReplace, pkgList); mConditionProviders.onPackagesChanged(queryReplace, pkgList); + mRankingHelper.onPackagesChanged(queryReplace, pkgList); } } }; @@ -1671,13 +1687,40 @@ public class NotificationManagerService extends SystemService { // Backup/restore interface @Override public byte[] getBackupPayload(int user) { - // TODO: build a payload of whatever is appropriate + if (DBG) Slog.d(TAG, "getBackupPayload u=" + user); + if (user != UserHandle.USER_OWNER) { + Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user); + return null; + } + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + writePolicyXml(baos, true /*forBackup*/); + return baos.toByteArray(); + } catch (IOException e) { + Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e); + } return null; } @Override public void applyRestore(byte[] payload, int user) { - // TODO: apply the restored payload as new current state + if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload=" + + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null)); + if (payload == null) { + Slog.w(TAG, "applyRestore: no payload to restore for user " + user); + return; + } + if (user != UserHandle.USER_OWNER) { + Slog.w(TAG, "applyRestore: cannot restore policy for user " + user); + return; + } + final ByteArrayInputStream bais = new ByteArrayInputStream(payload); + try { + readPolicyXml(bais, true /*forRestore*/); + savePolicyFile(); + } catch (NumberFormatException | XmlPullParserException | IOException e) { + Slog.w(TAG, "applyRestore: error reading payload", e); + } } @Override diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index 88055ba..e503ac8 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -17,6 +17,8 @@ package com.android.server.notification; import android.app.Notification; import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.os.Handler; import android.os.Message; import android.os.UserHandle; @@ -61,6 +63,7 @@ public class RankingHelper implements RankingConfig { private final ArrayMap<String, Record> mRecords = new ArrayMap<>(); // pkg|uid => Record private final ArrayMap<String, NotificationRecord> mProxyByGroupTmp = new ArrayMap<>(); + private final ArrayMap<String, Record> mRestoredWithoutUids = new ArrayMap<>(); // pkg => Record private final Context mContext; private final Handler mRankingHandler; @@ -119,12 +122,15 @@ public class RankingHelper implements RankingConfig { } } - public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException { + public void readXml(XmlPullParser parser, boolean forRestore) + throws XmlPullParserException, IOException { + final PackageManager pm = mContext.getPackageManager(); int type = parser.getEventType(); if (type != XmlPullParser.START_TAG) return; String tag = parser.getName(); if (!TAG_RANKING.equals(tag)) return; mRecords.clear(); + mRestoredWithoutUids.clear(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { tag = parser.getName(); if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) { @@ -132,21 +138,38 @@ public class RankingHelper implements RankingConfig { } if (type == XmlPullParser.START_TAG) { if (TAG_PACKAGE.equals(tag)) { - int uid = safeInt(parser, ATT_UID, UserHandle.USER_ALL); + int uid = safeInt(parser, ATT_UID, Record.UNKNOWN_UID); int priority = safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY); boolean peekable = safeBool(parser, ATT_PEEKABLE, DEFAULT_PEEKABLE); int vis = safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY); String name = parser.getAttributeValue(null, ATT_NAME); if (!TextUtils.isEmpty(name)) { + if (forRestore) { + try { + uid = pm.getPackageUid(name, UserHandle.USER_OWNER); + } catch (NameNotFoundException e) { + // noop + } + } + Record r = null; + if (uid == Record.UNKNOWN_UID) { + r = mRestoredWithoutUids.get(name); + if (r == null) { + r = new Record(); + mRestoredWithoutUids.put(name, r); + } + } else { + r = getOrCreateRecord(name, uid); + } if (priority != DEFAULT_PRIORITY) { - getOrCreateRecord(name, uid).priority = priority; + r.priority = priority; } if (peekable != DEFAULT_PEEKABLE) { - getOrCreateRecord(name, uid).peekable = peekable; + r.peekable = peekable; } if (vis != DEFAULT_VISIBILITY) { - getOrCreateRecord(name, uid).visibility = vis; + r.visibility = vis; } } } @@ -182,13 +205,16 @@ public class RankingHelper implements RankingConfig { } } - public void writeXml(XmlSerializer out) throws IOException { + public void writeXml(XmlSerializer out, boolean forBackup) throws IOException { out.startTag(null, TAG_RANKING); out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION)); final int N = mRecords.size(); for (int i = 0; i < N; i++) { final Record r = mRecords.valueAt(i); + if (forBackup && UserHandle.getUserId(r.uid) != UserHandle.USER_OWNER) { + continue; + } out.startTag(null, TAG_PACKAGE); out.attribute(null, ATT_NAME, r.pkg); if (r.priority != DEFAULT_PRIORITY) { @@ -200,7 +226,9 @@ public class RankingHelper implements RankingConfig { if (r.visibility != DEFAULT_VISIBILITY) { out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility)); } - out.attribute(null, ATT_UID, Integer.toString(r.uid)); + if (!forBackup) { + out.attribute(null, ATT_UID, Integer.toString(r.uid)); + } out.endTag(null, TAG_PACKAGE); } out.endTag(null, TAG_RANKING); @@ -364,15 +392,21 @@ public class RankingHelper implements RankingConfig { pw.print(prefix); pw.println("per-package config:"); } - final int N = mRecords.size(); + dumpRecords(pw, prefix, filter, mRecords); + dumpRecords(pw, prefix, filter, mRestoredWithoutUids); + } + + private static void dumpRecords(PrintWriter pw, String prefix, + NotificationManagerService.DumpFilter filter, ArrayMap<String, Record> records) { + final int N = records.size(); for (int i = 0; i < N; i++) { - final Record r = mRecords.valueAt(i); + final Record r = records.valueAt(i); if (filter == null || filter.matches(r.pkg)) { pw.print(prefix); pw.print(" "); pw.print(r.pkg); pw.print(" ("); - pw.print(r.uid); + pw.print(r.uid == Record.UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid)); pw.print(')'); if (r.priority != DEFAULT_PRIORITY) { pw.print(" priority="); @@ -391,11 +425,39 @@ public class RankingHelper implements RankingConfig { } } + public void onPackagesChanged(boolean queryReplace, String[] pkgList) { + if (queryReplace || pkgList == null || pkgList.length == 0 + || mRestoredWithoutUids.isEmpty()) { + return; // nothing to do + } + final PackageManager pm = mContext.getPackageManager(); + boolean updated = false; + for (String pkg : pkgList) { + final Record r = mRestoredWithoutUids.get(pkg); + if (r != null) { + try { + r.uid = pm.getPackageUid(r.pkg, UserHandle.USER_OWNER); + mRestoredWithoutUids.remove(pkg); + mRecords.put(recordKey(r.pkg, r.uid), r); + updated = true; + } catch (NameNotFoundException e) { + // noop + } + } + } + if (updated) { + updateConfig(); + } + } + private static class Record { + static int UNKNOWN_UID = UserHandle.USER_NULL; + String pkg; - int uid; + int uid = UNKNOWN_UID; int priority = DEFAULT_PRIORITY; boolean peekable = DEFAULT_PEEKABLE; int visibility = DEFAULT_VISIBILITY; } + } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 702c194..755b2ea 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -256,17 +256,27 @@ public class ZenModeHelper { } } - public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException { + public void readXml(XmlPullParser parser, boolean forRestore) + throws XmlPullParserException, IOException { final ZenModeConfig config = ZenModeConfig.readXml(parser, mConfigMigration); if (config != null) { + if (forRestore) { + if (config.user != UserHandle.USER_OWNER) { + return; + } + config.manualRule = null; // don't restore the manual rule + } if (DEBUG) Log.d(TAG, "readXml"); setConfig(config, "readXml"); } } - public void writeXml(XmlSerializer out) throws IOException { + public void writeXml(XmlSerializer out, boolean forBackup) throws IOException { final int N = mConfigs.size(); for (int i = 0; i < N; i++) { + if (forBackup && mConfigs.keyAt(i) != UserHandle.USER_OWNER) { + continue; + } mConfigs.valueAt(i).writeXml(out); } } |