summaryrefslogtreecommitdiffstats
path: root/services/core
diff options
context:
space:
mode:
Diffstat (limited to 'services/core')
-rw-r--r--services/core/java/com/android/server/EventLogTags.logtags8
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java4
-rw-r--r--services/core/java/com/android/server/LockSettingsService.java230
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java7
-rw-r--r--services/core/java/com/android/server/location/GpsLocationProvider.java10
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java8
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java209
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java23
-rw-r--r--services/core/java/com/android/server/notification/RankingHelper.java84
-rw-r--r--services/core/java/com/android/server/notification/ZenLog.java7
-rw-r--r--services/core/java/com/android/server/notification/ZenModeConditions.java13
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java58
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java19
13 files changed, 421 insertions, 259 deletions
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 4b65dec..c01d816 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -66,13 +66,13 @@ option java_package com.android.server
# when notifications are newly displayed on screen, or disappear from screen
27510 notification_visibility_changed (newlyVisibleKeys|3),(noLongerVisibleKeys|3)
# when notifications are expanded, or contracted
-27511 notification_expansion (key|3),(user_action|1),(expanded|1)
+27511 notification_expansion (key|3),(user_action|1),(expanded|1),(lifespan|1),(freshness|1),(exposure|1)
# when a notification has been clicked
-27520 notification_clicked (key|3)
+27520 notification_clicked (key|3),(lifespan|1),(freshness|1),(exposure|1)
# when a notification action button has been clicked
-27521 notification_action_clicked (key|3),(action_index|1)
+27521 notification_action_clicked (key|3),(action_index|1),(lifespan|1),(freshness|1),(exposure|1)
# when a notification has been canceled
-27530 notification_canceled (key|3),(reason|1),(lifespan|1),(exposure|1)
+27530 notification_canceled (key|3),(reason|1),(lifespan|1),(freshness|1),(exposure|1)
# replaces 27510 with a row per notification
27531 notification_visibility (key|3),(visibile|1),(lifespan|1),(freshness|1)
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 1f6c473..ab0c8a9 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -675,6 +675,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// We currently don't have a default input method... is
// one now available?
changed = chooseNewDefaultIMELocked();
+ } else if (!changed && isPackageModified(curIm.getPackageName())) {
+ // Even if the current input method is still available, mCurrentSubtype could
+ // be obsolete when the package is modified in practice.
+ changed = true;
}
if (changed) {
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 2df7f79..f6ca0d7 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -25,11 +25,9 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-
import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
import static android.content.Context.USER_SERVICE;
import static android.Manifest.permission.READ_PROFILE;
-
import android.database.sqlite.SQLiteDatabase;
import android.os.Binder;
import android.os.IBinder;
@@ -44,6 +42,7 @@ import android.provider.Settings;
import android.provider.Settings.Secure;
import android.provider.Settings.SettingNotFoundException;
import android.security.KeyStore;
+import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.text.TextUtils;
import android.util.Slog;
@@ -51,6 +50,7 @@ import android.util.Slog;
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.LockSettingsStorage.CredentialHash;
import java.util.Arrays;
@@ -76,6 +76,12 @@ public class LockSettingsService extends ILockSettings.Stub {
private boolean mFirstCallToVold;
private IGateKeeperService mGateKeeperService;
+ private interface CredentialUtil {
+ void setCredential(String credential, String savedCredential, int userId)
+ throws RemoteException;
+ byte[] toHash(String credential, int userId);
+ }
+
public LockSettingsService(Context context) {
mContext = context;
// Open the database
@@ -468,155 +474,163 @@ public class LockSettingsService extends ILockSettings.Stub {
byte[] toEnrollBytes = toEnroll == null
? null
: toEnroll.getBytes();
- byte[] hash = getGateKeeperService().enroll(userId, enrolledHandle, enrolledCredentialBytes,
- toEnrollBytes);
+ GateKeeperResponse response = getGateKeeperService().enroll(userId, enrolledHandle,
+ enrolledCredentialBytes, toEnrollBytes);
+ if (response == null) {
+ return null;
+ }
+
+ byte[] hash = response.getPayload();
if (hash != null) {
setKeystorePassword(toEnroll, userId);
+ } else {
+ // Should not happen
+ Slog.e(TAG, "Throttled while enrolling a password");
}
-
return hash;
}
@Override
- public boolean checkPattern(String pattern, int userId) throws RemoteException {
- try {
- doVerifyPattern(pattern, false, 0, userId);
- } catch (VerificationFailedException ex) {
- return false;
- }
-
- return true;
+ public VerifyCredentialResponse checkPattern(String pattern, int userId) throws RemoteException {
+ return doVerifyPattern(pattern, false, 0, userId);
}
@Override
- public byte[] verifyPattern(String pattern, long challenge, int userId)
+ public VerifyCredentialResponse verifyPattern(String pattern, long challenge, int userId)
throws RemoteException {
- try {
- return doVerifyPattern(pattern, true, challenge, userId);
- } catch (VerificationFailedException ex) {
- return null;
- }
+ return doVerifyPattern(pattern, true, challenge, userId);
}
- private byte[] doVerifyPattern(String pattern, boolean hasChallenge, long challenge,
- int userId) throws VerificationFailedException, RemoteException {
+ private VerifyCredentialResponse doVerifyPattern(String pattern, boolean hasChallenge, long challenge,
+ int userId) throws RemoteException {
checkPasswordReadPermission(userId);
-
CredentialHash storedHash = mStorage.readPatternHash(userId);
-
- if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(pattern)) {
- // don't need to pass empty passwords to GateKeeper
- return null;
- }
-
- if (TextUtils.isEmpty(pattern)) {
- throw new VerificationFailedException();
- }
-
- if (storedHash.version == CredentialHash.VERSION_LEGACY) {
- byte[] hash = mLockPatternUtils.patternToHash(
- mLockPatternUtils.stringToPattern(pattern));
- if (Arrays.equals(hash, storedHash.hash)) {
- unlockKeystore(pattern, userId);
- // migrate password to GateKeeper
- setLockPattern(pattern, null, userId);
- if (!hasChallenge) {
- return null;
- }
- // Fall through to get the auth token. Technically this should never happen,
- // as a user that had a legacy pattern would have to unlock their device
- // before getting to a flow with a challenge, but supporting for consistency.
- } else {
- throw new VerificationFailedException();
- }
- }
-
- byte[] token = null;
- if (hasChallenge) {
- token = getGateKeeperService()
- .verifyChallenge(userId, challenge, storedHash.hash, pattern.getBytes());
- if (token == null) {
- throw new VerificationFailedException();
- }
- } else if (!getGateKeeperService().verify(userId, storedHash.hash, pattern.getBytes())) {
- throw new VerificationFailedException();
- }
-
- // pattern has matched
- unlockKeystore(pattern, userId);
- return token;
-
+ return verifyCredential(userId, storedHash, pattern, hasChallenge,
+ challenge,
+ new CredentialUtil() {
+ @Override
+ public void setCredential(String pattern, String oldPattern, int userId)
+ throws RemoteException {
+ setLockPattern(pattern, oldPattern, userId);
+ }
+
+ @Override
+ public byte[] toHash(String pattern, int userId) {
+ return mLockPatternUtils.patternToHash(
+ mLockPatternUtils.stringToPattern(pattern));
+ }
+ }
+ );
}
@Override
- public boolean checkPassword(String password, int userId) throws RemoteException {
- try {
- doVerifyPassword(password, false, 0, userId);
- } catch (VerificationFailedException ex) {
- return false;
- }
- return true;
+ public VerifyCredentialResponse checkPassword(String password, int userId)
+ throws RemoteException {
+ return doVerifyPassword(password, false, 0, userId);
}
@Override
- public byte[] verifyPassword(String password, long challenge, int userId)
+ public VerifyCredentialResponse verifyPassword(String password, long challenge, int userId)
throws RemoteException {
- try {
- return doVerifyPassword(password, true, challenge, userId);
- } catch (VerificationFailedException ex) {
- return null;
- }
+ return doVerifyPassword(password, true, challenge, userId);
}
- private byte[] doVerifyPassword(String password, boolean hasChallenge, long challenge,
- int userId) throws VerificationFailedException, RemoteException {
+ private VerifyCredentialResponse doVerifyPassword(String password, boolean hasChallenge,
+ long challenge, int userId) throws RemoteException {
checkPasswordReadPermission(userId);
-
CredentialHash storedHash = mStorage.readPasswordHash(userId);
+ return verifyCredential(userId, storedHash, password, hasChallenge, challenge,
+ new CredentialUtil() {
+ @Override
+ public void setCredential(String password, String oldPassword, int userId)
+ throws RemoteException {
+ setLockPassword(password, oldPassword, userId);
+ }
- if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(password)) {
- // don't need to pass empty passwords to GateKeeper
- return null;
+ @Override
+ public byte[] toHash(String password, int userId) {
+ return mLockPatternUtils.passwordToHash(password, userId);
+ }
+ }
+ );
+ }
+
+ private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
+ String credential, boolean hasChallenge, long challenge, CredentialUtil credentialUtil)
+ throws RemoteException {
+ if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(credential)) {
+ // don't need to pass empty credentials to GateKeeper
+ return VerifyCredentialResponse.OK;
}
- if (TextUtils.isEmpty(password)) {
- throw new VerificationFailedException();
+ if (TextUtils.isEmpty(credential)) {
+ return VerifyCredentialResponse.ERROR;
}
if (storedHash.version == CredentialHash.VERSION_LEGACY) {
- byte[] hash = mLockPatternUtils.passwordToHash(password, userId);
+ byte[] hash = credentialUtil.toHash(credential, userId);
if (Arrays.equals(hash, storedHash.hash)) {
- unlockKeystore(password, userId);
- // migrate password to GateKeeper
- setLockPassword(password, null, userId);
+ unlockKeystore(credential, userId);
+ // migrate credential to GateKeeper
+ credentialUtil.setCredential(credential, null, userId);
if (!hasChallenge) {
- return null;
+ return VerifyCredentialResponse.OK;
}
// Fall through to get the auth token. Technically this should never happen,
- // as a user that had a legacy password would have to unlock their device
+ // as a user that had a legacy credential would have to unlock their device
// before getting to a flow with a challenge, but supporting for consistency.
} else {
- throw new VerificationFailedException();
+ return VerifyCredentialResponse.ERROR;
}
}
- byte[] token = null;
+ VerifyCredentialResponse response;
+ boolean shouldReEnroll = false;;
if (hasChallenge) {
- token = getGateKeeperService()
- .verifyChallenge(userId, challenge, storedHash.hash, password.getBytes());
- if (token == null) {
- throw new VerificationFailedException();
+ byte[] token = null;
+ GateKeeperResponse gateKeeperResponse = getGateKeeperService()
+ .verifyChallenge(userId, challenge, storedHash.hash, credential.getBytes());
+ int responseCode = gateKeeperResponse.getResponseCode();
+ if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
+ response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
+ } else if (responseCode == GateKeeperResponse.RESPONSE_OK) {
+ token = gateKeeperResponse.getPayload();
+ if (token == null) {
+ // something's wrong if there's no payload with a challenge
+ Slog.e(TAG, "verifyChallenge response had no associated payload");
+ response = VerifyCredentialResponse.ERROR;
+ } else {
+ shouldReEnroll = gateKeeperResponse.getShouldReEnroll();
+ response = new VerifyCredentialResponse(token);
+ }
+ } else {
+ response = VerifyCredentialResponse.ERROR;
+ }
+ } else {
+ GateKeeperResponse gateKeeperResponse = getGateKeeperService().verify(
+ userId, storedHash.hash, credential.getBytes());
+ int responseCode = gateKeeperResponse.getResponseCode();
+ if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
+ response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
+ } else if (responseCode == GateKeeperResponse.RESPONSE_OK) {
+ shouldReEnroll = gateKeeperResponse.getShouldReEnroll();
+ response = VerifyCredentialResponse.OK;
+ } else {
+ response = VerifyCredentialResponse.ERROR;
}
- } else if (!getGateKeeperService().verify(userId, storedHash.hash, password.getBytes())) {
- throw new VerificationFailedException();
}
- // password has matched
- unlockKeystore(password, userId);
- return token;
- }
+ if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
+ // credential has matched
+ unlockKeystore(credential, userId);
+ if (shouldReEnroll) {
+ credentialUtil.setCredential(credential, credential, userId);
+ }
+ }
+ return response;
+ }
@Override
public boolean checkVoldPassword(int userId) throws RemoteException {
@@ -644,7 +658,8 @@ public class LockSettingsService extends ILockSettings.Stub {
try {
if (mLockPatternUtils.isLockPatternEnabled(userId)) {
- if (checkPattern(password, userId)) {
+ if (checkPattern(password, userId).getResponseCode()
+ == GateKeeperResponse.RESPONSE_OK) {
return true;
}
}
@@ -653,7 +668,8 @@ public class LockSettingsService extends ILockSettings.Stub {
try {
if (mLockPatternUtils.isLockPasswordEnabled(userId)) {
- if (checkPassword(password, userId)) {
+ if (checkPassword(password, userId).getResponseCode()
+ == GateKeeperResponse.RESPONSE_OK) {
return true;
}
}
@@ -741,6 +757,4 @@ public class LockSettingsService extends ILockSettings.Stub {
return null;
}
- private class VerificationFailedException extends Exception {}
-
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e63be92..6e1b434 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -55,6 +55,7 @@ import android.graphics.Rect;
import android.os.BatteryStats;
import android.os.PersistableBundle;
import android.os.PowerManager;
+import android.os.Trace;
import android.os.TransactionTooLargeException;
import android.os.WorkSource;
import android.os.storage.IMountService;
@@ -3284,12 +3285,15 @@ public final class ActivityManagerService extends ActivityManagerNative
// the PID of the new process, or else throw a RuntimeException.
boolean isActivityProcess = (entryPoint == null);
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
+ app.processName);
checkTime(startTime, "startProcess: asking zygote to start proc");
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
checkTime(startTime, "startProcess: returned from zygote!");
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
if (app.isolated) {
mBatteryStatsService.addIsolatedUid(app.uid, app.info.uid);
@@ -3427,7 +3431,8 @@ public final class ActivityManagerService extends ActivityManagerNative
if (preferredComponent != null) {
ai = AppGlobals.getPackageManager().getActivityInfo(
preferredComponent, flags, userId);
- } else {
+ }
+ if (ai == null) {
ResolveInfo info = AppGlobals.getPackageManager().resolveIntent(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index d338d91..3850306 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -454,6 +454,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
updateNetworkState(networkState, info);
} else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)
+ || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)
|| Intent.ACTION_SCREEN_OFF.equals(action)
|| Intent.ACTION_SCREEN_ON.equals(action)) {
updateLowPowerMode();
@@ -501,14 +502,14 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
private void updateLowPowerMode() {
- final boolean disableGps;
+ // Disable GPS if we are in device idle mode.
+ boolean disableGps = mPowerManager.isDeviceIdleMode();
switch (Settings.Secure.getInt(mContext.getContentResolver(), BATTERY_SAVER_GPS_MODE,
BATTERY_SAVER_MODE_DISABLED_WHEN_SCREEN_OFF)) {
case BATTERY_SAVER_MODE_DISABLED_WHEN_SCREEN_OFF:
- disableGps = mPowerManager.isPowerSaveMode() && !mPowerManager.isInteractive();
+ // If we are in battery saver mode and the screen is off, disable GPS.
+ disableGps |= mPowerManager.isPowerSaveMode() && !mPowerManager.isInteractive();
break;
- default:
- disableGps = false;
}
if (disableGps != mDisableGps) {
mDisableGps = disableGps;
@@ -2034,6 +2035,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
intentFilter.addAction(ALARM_TIMEOUT);
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+ intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(SIM_STATE_CHANGED);
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 33c666a..40956c1 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -262,6 +262,14 @@ public class ConditionProviders extends ManagedServices {
return null;
}
+ public Condition findCondition(ComponentName component, Uri conditionId) {
+ if (component == null || conditionId == null) return null;
+ synchronized (mMutex) {
+ final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
+ return r != null ? r.condition : null;
+ }
+ }
+
public void ensureRecordExists(ComponentName component, Uri conditionId,
IConditionProvider provider) {
// constructed by convention, make sure the record exists...
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2be409a..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;
@@ -126,7 +131,6 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map.Entry;
-import java.util.NoSuchElementException;
import java.util.Objects;
/** {@hide} */
@@ -244,15 +248,18 @@ public class NotificationManagerService extends SystemService {
private Archive mArchive;
- // Notification control database. For now just contains disabled packages.
+ // Persistent storage for notification policy
private AtomicFile mPolicyFile;
+
+ // Temporary holder for <blocked-packages> config coming from old policy files.
private HashSet<String> mBlockedPackages = new HashSet<String>();
private static final int DB_VERSION = 1;
- private static final String TAG_BODY = "notification-policy";
+ private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
private static final String ATTR_VERSION = "version";
+ // Obsolete: converted if present, but not resaved to disk.
private static final String TAG_BLOCKED_PKGS = "blocked-packages";
private static final String TAG_PACKAGE = "package";
private static final String ATTR_NAME = "name";
@@ -310,53 +317,9 @@ public class NotificationManagerService extends SystemService {
mBuffer.addLast(nr.cloneLight());
}
- public void clear() {
- mBuffer.clear();
- }
-
public Iterator<StatusBarNotification> descendingIterator() {
return mBuffer.descendingIterator();
}
- public Iterator<StatusBarNotification> ascendingIterator() {
- return mBuffer.iterator();
- }
- public Iterator<StatusBarNotification> filter(
- final Iterator<StatusBarNotification> iter, final String pkg, final int userId) {
- return new Iterator<StatusBarNotification>() {
- StatusBarNotification mNext = findNext();
-
- private StatusBarNotification findNext() {
- while (iter.hasNext()) {
- StatusBarNotification nr = iter.next();
- if ((pkg == null || nr.getPackageName() == pkg)
- && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) {
- return nr;
- }
- }
- return null;
- }
-
- @Override
- public boolean hasNext() {
- return mNext == null;
- }
-
- @Override
- public StatusBarNotification next() {
- StatusBarNotification next = mNext;
- if (next == null) {
- throw new NoSuchElementException();
- }
- mNext = findNext();
- return next;
- }
-
- @Override
- public void remove() {
- iter.remove();
- }
- };
- }
public StatusBarNotification[] getArray(int count) {
if (count == 0) count = mBufferSize;
@@ -370,54 +333,48 @@ public class NotificationManagerService extends SystemService {
return a;
}
- public StatusBarNotification[] getArray(int count, String pkg, int userId) {
- if (count == 0) count = mBufferSize;
- final StatusBarNotification[] a
- = new StatusBarNotification[Math.min(count, mBuffer.size())];
- Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
- int i=0;
- while (iter.hasNext() && i < count) {
- a[i++] = iter.next();
+ }
+
+ 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;
+ }
+ }
+ }
}
- return a;
+ mZenModeHelper.readXml(parser, forRestore);
+ mRankingHelper.readXml(parser, forRestore);
}
-
}
private void loadPolicyFile() {
+ if (DBG) Slog.d(TAG, "loadPolicyFile");
synchronized(mPolicyFile) {
mBlockedPackages.clear();
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_BODY.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) {
@@ -438,7 +395,7 @@ public class NotificationManagerService extends SystemService {
}
private void handleSavePolicyFile() {
- Slog.d(TAG, "handleSavePolicyFile");
+ if (DBG) Slog.d(TAG, "handleSavePolicyFile");
synchronized (mPolicyFile) {
final FileOutputStream stream;
try {
@@ -449,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_BODY);
- out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
- mZenModeHelper.writeXml(out);
- mRankingHelper.writeXml(out);
- out.endTag(null, TAG_BODY);
- 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.
@@ -555,12 +517,15 @@ public class NotificationManagerService extends SystemService {
@Override
public void onNotificationClick(int callingUid, int callingPid, String key) {
synchronized (mNotificationList) {
- EventLogTags.writeNotificationClicked(key);
NotificationRecord r = mNotificationsByKey.get(key);
if (r == null) {
Log.w(TAG, "No notification with key: " + key);
return;
}
+ final long now = System.currentTimeMillis();
+ EventLogTags.writeNotificationClicked(key,
+ r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
+
StatusBarNotification sbn = r.sbn;
cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
sbn.getId(), Notification.FLAG_AUTO_CANCEL,
@@ -573,12 +538,14 @@ public class NotificationManagerService extends SystemService {
public void onNotificationActionClick(int callingUid, int callingPid, String key,
int actionIndex) {
synchronized (mNotificationList) {
- EventLogTags.writeNotificationActionClicked(key, actionIndex);
NotificationRecord r = mNotificationsByKey.get(key);
if (r == null) {
Log.w(TAG, "No notification with key: " + key);
return;
}
+ final long now = System.currentTimeMillis();
+ EventLogTags.writeNotificationActionClicked(key, actionIndex,
+ r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
// TODO: Log action click via UsageStats.
}
}
@@ -685,11 +652,14 @@ public class NotificationManagerService extends SystemService {
@Override
public void onNotificationExpansionChanged(String key,
boolean userAction, boolean expanded) {
- EventLogTags.writeNotificationExpansion(key, userAction ? 1 : 0, expanded ? 1 : 0);
synchronized (mNotificationList) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
r.stats.onExpansionChanged(userAction, expanded);
+ final long now = System.currentTimeMillis();
+ EventLogTags.writeNotificationExpansion(key,
+ userAction ? 1 : 0, expanded ? 1 : 0,
+ r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
}
}
}
@@ -767,6 +737,7 @@ public class NotificationManagerService extends SystemService {
}
mListeners.onPackagesChanged(queryReplace, pkgList);
mConditionProviders.onPackagesChanged(queryReplace, pkgList);
+ mRankingHelper.onPackagesChanged(queryReplace, pkgList);
}
}
};
@@ -806,8 +777,12 @@ public class NotificationManagerService extends SystemService {
// Refresh managed services
mConditionProviders.onUserSwitched(user);
mListeners.onUserSwitched(user);
+ mZenModeHelper.onUserSwitched(user);
} else if (action.equals(Intent.ACTION_USER_ADDED)) {
mUserProfiles.updateCache(context);
+ } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
+ final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ mZenModeHelper.onUserRemoved(user);
}
}
};
@@ -968,6 +943,7 @@ public class NotificationManagerService extends SystemService {
filter.addAction(Intent.ACTION_USER_STOPPED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
+ filter.addAction(Intent.ACTION_USER_REMOVED);
getContext().registerReceiver(mIntentReceiver, filter);
IntentFilter pkgFilter = new IntentFilter();
@@ -1418,8 +1394,6 @@ public class NotificationManagerService extends SystemService {
@Override
public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
long identity = Binder.clearCallingIdentity();
try {
synchronized (mNotificationList) {
@@ -1713,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
@@ -2787,10 +2788,8 @@ public class NotificationManagerService extends SystemService {
mArchive.record(r.sbn);
final long now = System.currentTimeMillis();
- final int lifespan = (int) (now - r.getCreationTimeMs());
- final long visibleSinceMs = r.getVisibleSinceMs();
- final int exposure = visibleSinceMs == 0L ? 0 : (int) (now - visibleSinceMs);
- EventLogTags.writeNotificationCanceled(canceledKey, reason, lifespan, exposure);
+ EventLogTags.writeNotificationCanceled(canceledKey, reason,
+ r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
}
/**
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index b8478c1..c4773ca 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -288,24 +288,27 @@ public final class NotificationRecord {
}
/**
- * Returns the timestamp of the most recent updates, or the post time if none.
+ * @param now this current time in milliseconds.
+ * @returns the number of milliseconds since the most recent update, or the post time if none.
*/
- public long getUpdateTimeMs() {
- return mUpdateTimeMs;
+ public int getFreshnessMs(long now) {
+ return (int) (now - mUpdateTimeMs);
}
/**
- * Returns the timestamp of the first post, ignoring updates.
+ * @param now this current time in milliseconds.
+ * @returns the number of milliseconds since the the first post, ignoring updates.
*/
- public long getCreationTimeMs() {
- return mCreationTimeMs;
+ public int getLifespanMs(long now) {
+ return (int) (now - mCreationTimeMs);
}
/**
- * Returns the timestamp of the most recent visibility event, or 0L if hidden.
+ * @param now this current time in milliseconds.
+ * @returns the number of milliseconds since the most recent visibility event, or 0 if never.
*/
- public long getVisibleSinceMs() {
- return mVisibleSinceMs;
+ public int getExposureMs(long now) {
+ return mVisibleSinceMs == 0 ? 0 : (int) (now - mVisibleSinceMs);
}
/**
@@ -313,7 +316,7 @@ public final class NotificationRecord {
*/
public void setVisibility(boolean visible) {
final long now = System.currentTimeMillis();
- mVisibleSinceMs = visible ? now : 0L;
+ mVisibleSinceMs = visible ? now : mVisibleSinceMs;
stats.onVisibilityChanged(visible);
EventLogTags.writeNotificationVisibility(getKey(), visible ? 1 : 0,
(int) (now - mCreationTimeMs),
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/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index 44fbd2d..c45071b 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -115,8 +115,11 @@ public class ZenLog {
append(TYPE_UNSUBSCRIBE, uri + "," + subscribeResult(provider, e));
}
- public static void traceConfig(String reason, ZenModeConfig newConfig) {
- append(TYPE_CONFIG, reason + "," + (newConfig != null ? newConfig.toString() : null));
+ public static void traceConfig(String reason, ZenModeConfig oldConfig,
+ ZenModeConfig newConfig) {
+ append(TYPE_CONFIG, reason
+ + "," + (newConfig != null ? newConfig.toString() : null)
+ + "," + ZenModeConfig.diff(oldConfig, newConfig));
}
public static void traceDisableEffects(NotificationRecord record, String reason) {
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index d6b7f2f..b89a654 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -63,7 +63,7 @@ public class ZenModeConditions implements ConditionProviders.Callback {
mConditionProviders.requestConditions(callback, relevance);
}
- public void evaluateConfig(ZenModeConfig config, boolean processSubscriptione) {
+ public void evaluateConfig(ZenModeConfig config, boolean processSubscriptions) {
if (config == null) return;
if (config.manualRule != null && config.manualRule.condition != null
&& !config.manualRule.isTrueOrUnknown()) {
@@ -71,16 +71,16 @@ public class ZenModeConditions implements ConditionProviders.Callback {
config.manualRule = null;
}
final ArraySet<Uri> current = new ArraySet<>();
- evaluateRule(config.manualRule, current, processSubscriptione);
+ evaluateRule(config.manualRule, current, processSubscriptions);
for (ZenRule automaticRule : config.automaticRules.values()) {
- evaluateRule(automaticRule, current, processSubscriptione);
+ evaluateRule(automaticRule, current, processSubscriptions);
updateSnoozing(automaticRule);
}
final int N = mSubscriptions.size();
for (int i = N - 1; i >= 0; i--) {
final Uri id = mSubscriptions.keyAt(i);
final ComponentName component = mSubscriptions.valueAt(i);
- if (processSubscriptione) {
+ if (processSubscriptions) {
if (!current.contains(id)) {
mConditionProviders.unsubscribeIfNecessary(component, id);
mSubscriptions.removeAt(i);
@@ -157,6 +157,11 @@ public class ZenModeConditions implements ConditionProviders.Callback {
if (DEBUG) Log.d(TAG, "zmc failed to subscribe");
}
}
+ if (rule.condition == null) {
+ rule.condition = mConditionProviders.findCondition(rule.component, rule.conditionId);
+ if (rule.condition != null && DEBUG) Log.d(TAG, "Found existing condition for: "
+ + rule.conditionId);
+ }
}
private boolean isAutomaticActive(ComponentName component) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 1860673..755b2ea 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -46,6 +46,7 @@ import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.service.notification.ZenModeConfig.ZenRule;
import android.util.ArraySet;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.R;
import com.android.server.LocalServices;
@@ -77,8 +78,10 @@ public class ZenModeHelper {
private final ZenModeFiltering mFiltering;
private final RingerModeDelegate mRingerModeDelegate = new RingerModeDelegate();
private final ZenModeConditions mConditions;
+ private final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>();
private int mZenMode;
+ private int mUser = UserHandle.USER_OWNER;
private ZenModeConfig mConfig;
private AudioManagerInternal mAudioManager;
private int mPreviousRingerMode = -1;
@@ -92,6 +95,7 @@ public class ZenModeHelper {
appendDefaultScheduleRules(mDefaultConfig);
appendDefaultEventRules(mDefaultConfig);
mConfig = mDefaultConfig;
+ mConfigs.put(UserHandle.USER_OWNER, mConfig);
mSettingsObserver = new SettingsObserver(mHandler);
mSettingsObserver.observe();
mFiltering = new ZenModeFiltering(mContext);
@@ -142,6 +146,25 @@ public class ZenModeHelper {
}
}
+ public void onUserSwitched(int user) {
+ if (mUser == user || user < UserHandle.USER_OWNER) return;
+ mUser = user;
+ if (DEBUG) Log.d(TAG, "onUserSwitched u=" + user);
+ ZenModeConfig config = mConfigs.get(user);
+ if (config == null) {
+ if (DEBUG) Log.d(TAG, "onUserSwitched: generating default config for user " + user);
+ config = mDefaultConfig.copy();
+ config.user = user;
+ }
+ setConfig(config, "onUserSwitched");
+ }
+
+ public void onUserRemoved(int user) {
+ if (user < UserHandle.USER_OWNER) return;
+ if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user);
+ mConfigs.remove(user);
+ }
+
public void requestZenModeConditions(IConditionListener callback, int relevance) {
mConditions.requestConditions(callback, relevance);
}
@@ -200,8 +223,13 @@ public class ZenModeHelper {
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mZenMode=");
pw.println(Global.zenModeToString(mZenMode));
- dump(pw, prefix, "mConfig", mConfig);
dump(pw, prefix, "mDefaultConfig", mDefaultConfig);
+ final int N = mConfigs.size();
+ for (int i = 0; i < N; i++) {
+ dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
+ }
+ pw.print(prefix); pw.print("mUser="); pw.println(mUser);
+ dump(pw, prefix, "mConfig", mConfig);
pw.print(prefix); pw.print("mPreviousRingerMode="); pw.println(mPreviousRingerMode);
pw.print(prefix); pw.print("mEffectsSuppressed="); pw.println(mEffectsSuppressed);
mFiltering.dump(pw, prefix);
@@ -228,16 +256,29 @@ 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 {
- mConfig.writeXml(out);
+ 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);
+ }
}
public Policy getNotificationPolicy() {
@@ -268,10 +309,17 @@ public class ZenModeHelper {
Log.w(TAG, "Invalid config in setConfig; " + config);
return false;
}
+ if (config.user != mUser) {
+ // simply store away for background users
+ mConfigs.put(config.user, config);
+ if (DEBUG) Log.d(TAG, "setConfig: store config for user " + config.user);
+ return true;
+ }
mConditions.evaluateConfig(config, false /*processSubscriptions*/); // may modify config
+ mConfigs.put(config.user, config);
if (config.equals(mConfig)) return true;
if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable());
- ZenLog.traceConfig(reason, config);
+ ZenLog.traceConfig(reason, mConfig, config);
final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
getNotificationPolicy(config));
mConfig = config;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f94c77b..671c44e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3642,15 +3642,24 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
+ private boolean canReceiveInput(WindowState win) {
+ boolean notFocusable =
+ (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0;
+ boolean altFocusableIm =
+ (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0;
+ boolean notFocusableForIm = notFocusable ^ altFocusableIm;
+ return !notFocusableForIm;
+ }
+
/** {@inheritDoc} */
@Override
public void layoutWindowLw(WindowState win, WindowState attached) {
- // we've already done the status bar
- final WindowManager.LayoutParams attrs = win.getAttrs();
- if ((win == mStatusBar && (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) == 0) ||
- win == mNavigationBar) {
+ // We've already done the navigation bar and status bar. If the status bar can receive
+ // input, we need to layout it again to accomodate for the IME window.
+ if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar) {
return;
}
+ final WindowManager.LayoutParams attrs = win.getAttrs();
final boolean isDefaultDisplay = win.isDefaultDisplay();
final boolean needsToOffsetInputMethodTarget = isDefaultDisplay &&
(win == mLastInputMethodTargetWindow && mLastInputMethodWindow != null);
@@ -3717,7 +3726,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
+ mUnrestrictedScreenHeight;
cf.bottom = vf.bottom = mStableBottom;
cf.top = vf.top = mStableTop;
- } else if (win == mStatusBar && (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
+ } else if (win == mStatusBar) {
pf.left = df.left = of.left = mUnrestrictedScreenLeft;
pf.top = df.top = of.top = mUnrestrictedScreenTop;
pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;