diff options
Diffstat (limited to 'services/core')
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; |