From bfdb44047aa434c0259d17da0560463bc7c1c1d5 Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Wed, 27 Sep 2017 11:05:29 -0400 Subject: [PATCH 001/226] Apply texture matrix to the layer canvas Fix camera preview shown inverted. Apply texture matrix when drawing opengl layer. Test: Ran camera preview in message app and smart lock with face. Ran CtsUiRenderingTestCases and CtsGraphicsTestCases tests. Bug: 65534412 Change-Id: I3ed52a6c62921fcdfe30104dd176802ed1533fad (cherry picked from commit 944dbf255ab9ddeb829830268b0af0961c257150) --- libs/hwui/pipeline/skia/LayerDrawable.cpp | 40 ++++++++++++++--------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index 17438e5e1cdc4..5c6078d566b4a 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -36,22 +36,18 @@ void LayerDrawable::onDraw(SkCanvas* canvas) { bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer) { // transform the matrix based on the layer - int saveCount = -1; - if (!layer->getTransform().isIdentity()) { - saveCount = canvas->save(); - SkMatrix transform; - layer->getTransform().copyTo(transform); - canvas->concat(transform); - } - + SkMatrix layerTransform; + layer->getTransform().copyTo(layerTransform); sk_sp layerImage; + int layerWidth = layer->getWidth(); + int layerHeight = layer->getHeight(); if (layer->getApi() == Layer::Api::OpenGL) { GlLayer* glLayer = static_cast(layer); GrGLTextureInfo externalTexture; externalTexture.fTarget = glLayer->getRenderTarget(); externalTexture.fID = glLayer->getTextureId(); - GrBackendTexture backendTexture(glLayer->getWidth(), glLayer->getHeight(), - kRGBA_8888_GrPixelConfig, externalTexture); + GrBackendTexture backendTexture(layerWidth, layerHeight, kRGBA_8888_GrPixelConfig, + externalTexture); layerImage = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, nullptr); } else { @@ -62,15 +58,29 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer } if (layerImage) { + SkMatrix textureMatrix; + layer->getTexTransform().copyTo(textureMatrix); + //TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed + // use bottom left origin and remove flipV and invert transformations. + SkMatrix flipV; + flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1); + textureMatrix.preConcat(flipV); + textureMatrix.preScale(1.0f/layerWidth, 1.0f/layerHeight); + textureMatrix.postScale(layerWidth, layerHeight); + SkMatrix textureMatrixInv; + if (!textureMatrix.invert(&textureMatrixInv)) { + textureMatrixInv = textureMatrix; + } + + SkMatrix matrix = SkMatrix::Concat(textureMatrixInv, layerTransform); + SkPaint paint; paint.setAlpha(layer->getAlpha()); paint.setBlendMode(layer->getMode()); paint.setColorFilter(sk_ref_sp(layer->getColorFilter())); - canvas->drawImage(layerImage, 0, 0, &paint); - } - // restore the original matrix - if (saveCount >= 0) { - canvas->restoreToCount(saveCount); + // draw image with a shader to avoid save/restore of the matrix + paint.setShader(layerImage->makeShader(&matrix)); + canvas->drawRect(SkRect::MakeWH(layerWidth, layerHeight), paint); } return layerImage; From ff56feab5a824cba486b5b03780c5339304aa87a Mon Sep 17 00:00:00 2001 From: Bernardo Rufino Date: Tue, 3 Oct 2017 13:55:10 +0100 Subject: [PATCH 002/226] Canonicalize notification channel sounds for backup Canonicalize for backup and canonicalize and uncanonicalize for restore (see comment). Test: Set custom notification sound, make backup, remove notification sound from device (from Ringtones and make sure to update media content provider), restore => Observe default instead of random number. Do the same without removing the sound and observe restores successfully. Test: runtest systemui-notification Bug: 66444697 (cherry picked from commit c27bb6ad34fd59161f6460b692ba72c7ede789b6) Change-Id: I32c186d0d7479b01f6cc67cce9bc5cb66264a064 (cherry picked from commit 2d7a4a3f674d62adfb2825b738f2e87ec246194d) --- core/java/android/app/NotificationChannel.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 47063f0842e88..c06ad3f32cf4e 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -32,6 +32,8 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.util.Preconditions; +import com.android.internal.util.Preconditions; + import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; From bca4aa1c9a28024b51d8acb67a2ccf6eca93e54b Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Tue, 10 Oct 2017 13:58:49 -0400 Subject: [PATCH 003/226] Send a11y updates for updated notifications Unless the updated notification is quiet. Test: runtest systemui-notification Change-Id: I0d0b27cedf085c00648d00ed63f252f94d8e5e35 Fixes: 67626983 (cherry picked from commit 9418756a11162efd78cb0a6fc87873ee15252bff) --- .../NotificationManagerService.java | 31 +++++++--- .../notification/BuzzBeepBlinkTest.java | 57 +++++++++++++++++-- .../statusbartest/NotificationTestList.java | 3 + 3 files changed, 77 insertions(+), 14 deletions(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 14cd055506a5c..168f070a77289 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -282,6 +282,7 @@ public class NotificationManagerService extends SystemService { private WindowManagerInternal mWindowManagerInternal; private AlarmManager mAlarmManager; private ICompanionDeviceManager mCompanionManager; + private AccessibilityManager mAccessibilityManager; final IBinder mForegroundToken = new Binder(); private WorkerHandler mHandler; @@ -1221,6 +1222,12 @@ public class NotificationManagerService extends SystemService { mUsageStats = us; } + @VisibleForTesting + void setAccessibilityManager(AccessibilityManager am) { + mAccessibilityManager = am; + } + + // TODO: All tests should use this init instead of the one-off setters above. @VisibleForTesting void init(Looper looper, IPackageManager packageManager, @@ -1235,6 +1242,8 @@ public class NotificationManagerService extends SystemService { Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE); + mAccessibilityManager = + (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); mAm = ActivityManager.getService(); mPackageManager = packageManager; mPackageManagerClient = packageManagerClient; @@ -4117,13 +4126,16 @@ public class NotificationManagerService extends SystemService { // These are set inside the conditional if the notification is allowed to make noise. boolean hasValidVibrate = false; boolean hasValidSound = false; + boolean sentAccessibilityEvent = false; + // If the notification will appear in the status bar, it should send an accessibility + // event + if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) { + sendAccessibilityEvent(notification, record.sbn.getPackageName()); + sentAccessibilityEvent = true; + } if (aboveThreshold && isNotificationForCurrentUser(record)) { - // If the notification will appear in the status bar, it should send an accessibility - // event - if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) { - sendAccessibilityEvent(notification, record.sbn.getPackageName()); - } + if (mSystemReady && mAudioManager != null) { Uri soundUri = record.getSound(); hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri); @@ -4141,6 +4153,10 @@ public class NotificationManagerService extends SystemService { boolean hasAudibleAlert = hasValidSound || hasValidVibrate; if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) { + if (!sentAccessibilityEvent) { + sendAccessibilityEvent(notification, record.sbn.getPackageName()); + sentAccessibilityEvent = true; + } if (DBG) Slog.v(TAG, "Interrupting!"); if (hasValidSound) { mSoundNotificationKey = key; @@ -4661,8 +4677,7 @@ public class NotificationManagerService extends SystemService { } void sendAccessibilityEvent(Notification notification, CharSequence packageName) { - AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); - if (!manager.isEnabled()) { + if (!mAccessibilityManager.isEnabled()) { return; } @@ -4676,7 +4691,7 @@ public class NotificationManagerService extends SystemService { event.getText().add(tickerText); } - manager.sendAccessibilityEvent(event); + mAccessibilityManager.sendAccessibilityEvent(event); } /** diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java index 9fa1d68b86282..0b4d61fb783e0 100644 --- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -19,6 +19,7 @@ import static android.app.Notification.GROUP_ALERT_ALL; import static android.app.Notification.GROUP_ALERT_CHILDREN; import static android.app.Notification.GROUP_ALERT_SUMMARY; import static android.app.NotificationManager.IMPORTANCE_HIGH; +import static android.app.NotificationManager.IMPORTANCE_MIN; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; @@ -57,7 +58,13 @@ import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; +import android.util.Slog; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.IAccessibilityManager; +import android.view.accessibility.IAccessibilityManagerClient; +import com.android.internal.util.IntPair; import com.android.server.lights.Light; import org.junit.Before; @@ -67,6 +74,8 @@ import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; @SmallTest @RunWith(AndroidJUnit4.class) @@ -80,6 +89,8 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { NotificationManagerService.WorkerHandler mHandler; @Mock NotificationUsageStats mUsageStats; + @Mock + IAccessibilityManager mAccessibilityService; private NotificationManagerService mService; private String mPkg = "com.android.server.notification"; @@ -111,17 +122,25 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { private static final int MAX_VIBRATION_DELAY = 1000; @Before - public void setUp() { + public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mAudioManager.isAudioFocusExclusive()).thenReturn(false); when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer); when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10); when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); - when(mUsageStats.isAlertRateLimited(any())).thenReturn(false); - mService = new NotificationManagerService(getContext()); + long serviceReturnValue = IntPair.of( + AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED, + AccessibilityEvent.TYPES_ALL_MASK); + when(mAccessibilityService.addClient(any(), anyInt())).thenReturn(serviceReturnValue); + AccessibilityManager accessibilityManager = + new AccessibilityManager(Handler.getMain(), mAccessibilityService, 0); + verify(mAccessibilityService).addClient(any(IAccessibilityManagerClient.class), anyInt()); + assertTrue(accessibilityManager.isEnabled()); + + mService = spy(new NotificationManagerService(getContext())); mService.setAudioManager(mAudioManager); mService.setVibrator(mVibrator); mService.setSystemReady(true); @@ -130,6 +149,7 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { mService.setScreenOn(false); mService.setFallbackVibrationPattern(FALLBACK_VIBRATION_PATTERN); mService.setUsageStats(mUsageStats); + mService.setAccessibilityManager(accessibilityManager); } // @@ -381,6 +401,7 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { verifyBeepLooped(); verifyNeverVibrate(); + verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt()); } @Test @@ -435,6 +456,7 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { r.isUpdate = true; mService.buzzBeepBlinkLocked(r); verifyBeepLooped(); + verify(mAccessibilityService, times(2)).sendAccessibilityEvent(any(), anyInt()); } @Test @@ -450,6 +472,7 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { // update should not beep mService.buzzBeepBlinkLocked(s); verifyNeverBeep(); + verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt()); } @Test @@ -547,7 +570,7 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { mService.mInCall = true; mService.buzzBeepBlinkLocked(r); - //verify(mService, times(1)).playInCallNotification(); + verify(mService, times(1)).playInCallNotification(); verifyNeverBeep(); // doesn't play normal beep } @@ -842,7 +865,6 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { mService.addNotification(r); mService.buzzBeepBlinkLocked(r); - verifyNeverBeep(); } @@ -870,7 +892,6 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY; mService.buzzBeepBlinkLocked(summary); - verify(mUsageStats, never()).isAlertRateLimited(any()); } @@ -889,6 +910,30 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { verifyNeverBeep(); } + @Test + public void testA11yMinInitialPost() throws Exception { + NotificationRecord r = getQuietNotification(); + r.setImportance(IMPORTANCE_MIN, ""); + mService.buzzBeepBlinkLocked(r); + verify(mAccessibilityService, never()).sendAccessibilityEvent(any(), anyInt()); + } + + @Test + public void testA11yQuietInitialPost() throws Exception { + NotificationRecord r = getQuietNotification(); + mService.buzzBeepBlinkLocked(r); + verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt()); + } + + @Test + public void testA11yQuietUpdate() throws Exception { + NotificationRecord r = getQuietNotification(); + mService.buzzBeepBlinkLocked(r); + r.isUpdate = true; + mService.buzzBeepBlinkLocked(r); + verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt()); + } + static class VibrateRepeatMatcher implements ArgumentMatcher { private final int mRepeatIndex; diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java index 82104034ca6ea..163250d9d666f 100644 --- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java +++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java @@ -296,6 +296,7 @@ public class NotificationTestList extends TestActivity Notification n = new Notification.Builder(NotificationTestList.this, "min") .setSmallIcon(R.drawable.icon2) .setContentTitle("Min priority") + .setTicker("Min priority") .build(); mNM.notify("min", 7000, n); } @@ -306,6 +307,7 @@ public class NotificationTestList extends TestActivity Notification n = new Notification.Builder(NotificationTestList.this, "low") .setSmallIcon(R.drawable.icon2) .setContentTitle("Low priority") + .setTicker("Low priority") .build(); mNM.notify("low", 7002, n); } @@ -326,6 +328,7 @@ public class NotificationTestList extends TestActivity Notification n = new Notification.Builder(NotificationTestList.this, "high") .setSmallIcon(R.drawable.icon2) .setContentTitle("High priority") + .setTicker("High priority") .build(); mNM.notify("high", 7006, n); } From 94c06d6cd493d349261add3f0190e08e1ead18e8 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Wed, 11 Oct 2017 08:44:46 -0400 Subject: [PATCH 004/226] Check target api version before crashing apps. All foreground service notifications need to be posted to a valid notification channel, but only crash apps that don't provide a valid notification if they target O_MR1+. Fixes: 66905243 Test: manual, open apps with invalid notifications that do and don't target O_MR1. Change-Id: I60897302bf9806cba0e0be365e65c5e8c4ef4806 (cherry picked from commit 5a4399a34cd73d80041348463fd6e9d5954f77ef) --- .../com/android/server/am/ServiceRecord.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index ac85e6b132bf1..16995e50fdbf9 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -33,6 +33,7 @@ import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; @@ -517,14 +518,27 @@ final class ServiceRecord extends Binder { } catch (PackageManager.NameNotFoundException e) { } } - if (localForegroundNoti.getSmallIcon() == null - || nm.getNotificationChannel(localPackageName, appUid, + if (nm.getNotificationChannel(localPackageName, appUid, localForegroundNoti.getChannelId()) == null) { + int targetSdkVersion = Build.VERSION_CODES.O_MR1; + try { + final ApplicationInfo applicationInfo = + ams.mContext.getPackageManager().getApplicationInfoAsUser( + appInfo.packageName, 0, userId); + targetSdkVersion = applicationInfo.targetSdkVersion; + } catch (PackageManager.NameNotFoundException e) { + } + if (targetSdkVersion >= Build.VERSION_CODES.O_MR1) { + throw new RuntimeException( + "invalid channel for service notification: " + + foregroundNoti); + } + } + if (localForegroundNoti.getSmallIcon() == null) { // Notifications whose icon is 0 are defined to not show // a notification, silently ignoring it. We don't want to // just ignore it, we want to prevent the service from // being foreground. - // Also every notification needs a channel. throw new RuntimeException("invalid service notification: " + foregroundNoti); } From ff25c19ac398a83f2df2430377551d35246fc65a Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Wed, 11 Oct 2017 15:50:51 -0700 Subject: [PATCH 005/226] Fix backup server Mea culpa. Properly do the trampoline indirection dance so that we can asynchronously bring up the service components. We'd previously accidentally introduced an ordering problem such that the init process... didn't. Bug: 67676879 Test: 'adb shell dumpsys backup' and 'adb shell bmgr list transports' after boot Change-Id: I3e9a904a009f4745727e5eb13f7307c6deda1e4f (cherry picked from commit 8808609896023165274092265cc2b51597d6a388) --- .../server/backup/BackupManagerService.java | 67 ++++++++---------- .../RefactoredBackupManagerService.java | 68 ++++++++----------- .../com/android/server/backup/Trampoline.java | 29 ++++++-- 3 files changed, 82 insertions(+), 82 deletions(-) diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index eabe21fed9df8..f9213aabe2725 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -319,7 +319,6 @@ public class BackupManagerService implements BackupManagerServiceInterface { boolean mProvisioned; boolean mAutoRestore; PowerManager.WakeLock mWakelock; - HandlerThread mHandlerThread; BackupHandler mBackupHandler; PendingIntent mRunBackupIntent, mRunInitIntent; BroadcastReceiver mRunBackupReceiver, mRunInitReceiver; @@ -409,43 +408,37 @@ public class BackupManagerService implements BackupManagerServiceInterface { // Called through the trampoline from onUnlockUser(), then we buck the work // off to the background thread to keep the unlock time down. public void unlockSystemUser() { - mBackupHandler.post(() -> { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init"); - sInstance.initialize(UserHandle.USER_SYSTEM); - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - - // Migrate legacy setting - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate"); - if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) { + // Migrate legacy setting + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate"); + if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) { + if (DEBUG) { + Slog.i(TAG, "Backup enable apparently not migrated"); + } + final ContentResolver r = sInstance.mContext.getContentResolver(); + final int enableState = Settings.Secure.getIntForUser(r, + Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM); + if (enableState >= 0) { if (DEBUG) { - Slog.i(TAG, "Backup enable apparently not migrated"); + Slog.i(TAG, "Migrating enable state " + (enableState != 0)); } - final ContentResolver r = sInstance.mContext.getContentResolver(); - final int enableState = Settings.Secure.getIntForUser(r, - Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM); - if (enableState >= 0) { - if (DEBUG) { - Slog.i(TAG, "Migrating enable state " + (enableState != 0)); - } - writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM); - Settings.Secure.putStringForUser(r, - Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM); - } else { - if (DEBUG) { - Slog.i(TAG, "Backup not yet configured; retaining null enable state"); - } + writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM); + Settings.Secure.putStringForUser(r, + Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM); + } else { + if (DEBUG) { + Slog.i(TAG, "Backup not yet configured; retaining null enable state"); } } - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable"); - try { - sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM)); - } catch (RemoteException e) { - // can't happen; it's a local object - } - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - }); + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable"); + try { + sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM)); + } catch (RemoteException e) { + // can't happen; it's a local object + } + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } class ProvisionedObserver extends ContentObserver { @@ -1220,7 +1213,7 @@ public class BackupManagerService implements BackupManagerServiceInterface { // ----- Main service implementation ----- - public BackupManagerService(Context context, Trampoline parent) { + public BackupManagerService(Context context, Trampoline parent, HandlerThread backupThread) { mContext = context; mPackageManager = context.getPackageManager(); mPackageManagerBinder = AppGlobals.getPackageManager(); @@ -1233,9 +1226,7 @@ public class BackupManagerService implements BackupManagerServiceInterface { mBackupManagerBinder = Trampoline.asInterface(parent.asBinder()); // spin up the backup/restore handler thread - mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND); - mHandlerThread.start(); - mBackupHandler = new BackupHandler(mHandlerThread.getLooper()); + mBackupHandler = new BackupHandler(backupThread.getLooper()); // Set up our bookkeeping final ContentResolver resolver = context.getContentResolver(); @@ -1360,7 +1351,7 @@ public class BackupManagerService implements BackupManagerServiceInterface { if (DEBUG) Slog.v(TAG, "Starting with transport " + currentTransport); mTransportManager = new TransportManager(context, transportWhitelist, currentTransport, - mTransportBoundListener, mHandlerThread.getLooper()); + mTransportBoundListener, backupThread.getLooper()); mTransportManager.registerAllTransports(); // Now that we know about valid backup participants, parse any diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java index f298065903a80..20f236908112e 100644 --- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java @@ -237,7 +237,6 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter private boolean mProvisioned; private boolean mAutoRestore; private PowerManager.WakeLock mWakelock; - private HandlerThread mHandlerThread; private BackupHandler mBackupHandler; private PendingIntent mRunBackupIntent; private PendingIntent mRunInitIntent; @@ -556,43 +555,37 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter // Called through the trampoline from onUnlockUser(), then we buck the work // off to the background thread to keep the unlock time down. public void unlockSystemUser() { - mBackupHandler.post(() -> { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init"); - sInstance.initialize(UserHandle.USER_SYSTEM); - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - - // Migrate legacy setting - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate"); - if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) { + // Migrate legacy setting + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate"); + if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) { + if (DEBUG) { + Slog.i(TAG, "Backup enable apparently not migrated"); + } + final ContentResolver r = sInstance.mContext.getContentResolver(); + final int enableState = Settings.Secure.getIntForUser(r, + Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM); + if (enableState >= 0) { if (DEBUG) { - Slog.i(TAG, "Backup enable apparently not migrated"); + Slog.i(TAG, "Migrating enable state " + (enableState != 0)); } - final ContentResolver r = sInstance.mContext.getContentResolver(); - final int enableState = Settings.Secure.getIntForUser(r, - Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM); - if (enableState >= 0) { - if (DEBUG) { - Slog.i(TAG, "Migrating enable state " + (enableState != 0)); - } - writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM); - Settings.Secure.putStringForUser(r, - Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM); - } else { - if (DEBUG) { - Slog.i(TAG, "Backup not yet configured; retaining null enable state"); - } + writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM); + Settings.Secure.putStringForUser(r, + Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM); + } else { + if (DEBUG) { + Slog.i(TAG, "Backup not yet configured; retaining null enable state"); } } - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable"); - try { - sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM)); - } catch (RemoteException e) { - // can't happen; it's a local object - } - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - }); + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable"); + try { + sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM)); + } catch (RemoteException e) { + // can't happen; it's a local object + } + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } // Bookkeeping of in-flight operations for timeout etc. purposes. The operation @@ -729,7 +722,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter // ----- Main service implementation ----- - public RefactoredBackupManagerService(Context context, Trampoline parent) { + public RefactoredBackupManagerService(Context context, Trampoline parent, + HandlerThread backupThread) { mContext = context; mPackageManager = context.getPackageManager(); mPackageManagerBinder = AppGlobals.getPackageManager(); @@ -742,9 +736,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter mBackupManagerBinder = Trampoline.asInterface(parent.asBinder()); // spin up the backup/restore handler thread - mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND); - mHandlerThread.start(); - mBackupHandler = new BackupHandler(this, mHandlerThread.getLooper()); + mBackupHandler = new BackupHandler(this, backupThread.getLooper()); // Set up our bookkeeping final ContentResolver resolver = context.getContentResolver(); @@ -824,7 +816,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter if (DEBUG) Slog.v(TAG, "Starting with transport " + currentTransport); mTransportManager = new TransportManager(context, transportWhitelist, currentTransport, - mTransportBoundListener, mHandlerThread.getLooper()); + mTransportBoundListener, backupThread.getLooper()); mTransportManager.registerAllTransports(); // Now that we know about valid backup participants, parse any diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java index 9739e38055b8b..9847edf8f8ce1 100644 --- a/services/backup/java/com/android/server/backup/Trampoline.java +++ b/services/backup/java/com/android/server/backup/Trampoline.java @@ -28,11 +28,15 @@ import android.content.Context; import android.content.Intent; import android.os.Binder; import android.os.Environment; +import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; +import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; +import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; @@ -75,6 +79,8 @@ public class Trampoline extends IBackupManager.Stub { final boolean mGlobalDisable; volatile BackupManagerServiceInterface mService; + private HandlerThread mHandlerThread; + public Trampoline(Context context) { mContext = context; mGlobalDisable = isBackupDisabled(); @@ -111,11 +117,11 @@ public class Trampoline extends IBackupManager.Stub { } protected BackupManagerServiceInterface createRefactoredBackupManagerService() { - return new RefactoredBackupManagerService(mContext, this); + return new RefactoredBackupManagerService(mContext, this, mHandlerThread); } protected BackupManagerServiceInterface createBackupManagerService() { - return new BackupManagerService(mContext, this); + return new BackupManagerService(mContext, this, mHandlerThread); } // internal control API @@ -140,10 +146,21 @@ public class Trampoline extends IBackupManager.Stub { } void unlockSystemUser() { - BackupManagerServiceInterface svc = mService; - if (svc != null) { - svc.unlockSystemUser(); - } + mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND); + mHandlerThread.start(); + + Handler h = new Handler(mHandlerThread.getLooper()); + h.post(() -> { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init"); + initialize(UserHandle.USER_SYSTEM); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + + BackupManagerServiceInterface svc = mService; + Slog.i(TAG, "Unlocking system user; mService=" + mService); + if (svc != null) { + svc.unlockSystemUser(); + } + }); } public void setBackupServiceActive(final int userHandle, boolean makeActive) { From 9f2c9df518cb1e6bed97814a88889f0e8525ac43 Mon Sep 17 00:00:00 2001 From: Tun Zheng Date: Thu, 12 Oct 2017 11:56:50 -0700 Subject: [PATCH 006/226] Fix group divider make chrome crash issue. ListMenuItemView's group divider is only existed in popup_menu_item_layout, but Chrome use list_menu_item_layout. It will make NullPointerException and crash the chrome. Fix it by check null before using the group divider. Bug: 66987086 Test: Long click an image from webpage in chrome, click the "Download image" item, and it works well. Change-Id: Ie5f19194a968b4fff0126e1cf8bebda5344c8105 (cherry picked from commit 670ceded12e09219c41f4931b23d1baab6ab2fb0) --- .../com/android/internal/view/menu/ListMenuItemView.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java index f76c7247aba93..8f80bfe3fb501 100644 --- a/core/java/com/android/internal/view/menu/ListMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java @@ -319,13 +319,15 @@ public class ListMenuItemView extends LinearLayout public void setGroupDividerEnabled(boolean groupDividerEnabled) { // If mHasListDivider is true, disabling the groupDivider. // Otherwise, checking enbling it according to groupDividerEnabled flag. - mGroupDivider.setVisibility(!mHasListDivider - && groupDividerEnabled ? View.VISIBLE : View.GONE); + if (mGroupDivider != null) { + mGroupDivider.setVisibility(!mHasListDivider + && groupDividerEnabled ? View.VISIBLE : View.GONE); + } } @Override public void adjustListItemSelectionBounds(Rect rect) { - if (mGroupDivider.getVisibility() == View.VISIBLE) { + if (mGroupDivider != null && mGroupDivider.getVisibility() == View.VISIBLE) { // groupDivider is a part of MenuItemListView. // If ListMenuItem with divider enabled is hovered/clicked, divider also gets selected. // Clipping the selector bounds from the top divider portion when divider is enabled, From eaf0064767b9f463b620c36530212004ef3e4d79 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 18 Oct 2017 13:49:42 -0700 Subject: [PATCH 007/226] Fix NPE when getting managed profile info. - There can be a race condition where the cached profile user ids aren't yet updated but the actual user has already been removed. Ignore these users when computing the quiet profile users. Bug: 67932220 Test: Add/remove a work profile and launch an app Change-Id: I8888d30e431ca4ffdacc28cc15a80a2deaa23d10 (cherry picked from commit 129771a08fa29302af2b491c8c81779becb936c3) --- services/core/java/com/android/server/am/RecentTasks.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java index 78274bdc0e038..ed3f5033a0467 100644 --- a/services/core/java/com/android/server/am/RecentTasks.java +++ b/services/core/java/com/android/server/am/RecentTasks.java @@ -914,7 +914,7 @@ class RecentTasks { mTmpQuietProfileUserIds.clear(); for (int userId : profileUserIds) { final UserInfo userInfo = mUserController.getUserInfo(userId); - if (userInfo.isManagedProfile() && userInfo.isQuietModeEnabled()) { + if (userInfo != null && userInfo.isManagedProfile() && userInfo.isQuietModeEnabled()) { mTmpQuietProfileUserIds.put(userId, true); } if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "User: " + userInfo From a00210db5dafc938095410baa1f5381fea8fa6fd Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Wed, 18 Oct 2017 18:46:27 -0700 Subject: [PATCH 008/226] Fix invisible SurfaceViews Of course OPAQUE != SURFACE_OPAQUE: that would be too easy. Test: Manual Bug: 67896876 Change-Id: Ic7aaae42c7de8195c474c39d850f2a07a58ccc9d (cherry picked from commit 2b8a5e11dc452c11a18eaaab70b6c268d46b8ddd) --- core/java/android/view/SurfaceControl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 548258957338e..ff027a947d070 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -1161,9 +1161,9 @@ public class SurfaceControl { */ public Transaction setOpaque(SurfaceControl sc, boolean isOpaque) { if (isOpaque) { - nativeSetFlags(mNativeObject, sc.mNativeObject, OPAQUE, OPAQUE); + nativeSetFlags(mNativeObject, sc.mNativeObject, SURFACE_OPAQUE, SURFACE_OPAQUE); } else { - nativeSetFlags(mNativeObject, sc.mNativeObject, 0, OPAQUE); + nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SURFACE_OPAQUE); } return this; } From cad25b3f0660499b8815e545e87d23506d202710 Mon Sep 17 00:00:00 2001 From: Todd Kennedy Date: Tue, 31 Oct 2017 13:05:55 -0700 Subject: [PATCH 009/226] Set updated system package's mExtra While scanning system packages, we abort the process in the middle of the scan and leave our internal structures in an ill defined state. This change is not about making the structures consistent; rather unblocking a build breakage. We'll look at data consistency during a later refactoring stage when the process of package scanning is fixed. A reboot is necessary for the bug to exhibit itself because the permissions are re-granted [or denied] on every boot. Change-Id: I96cc2f76623911f2cf93727e9f1787b42210a8d6 Fixes: 68328870 Test: Manual. Test: Add a new priv-app that take a privieleged permission. Test: See that the permission is granted. Test: Update the app and reboot Test: See that the permission is still granted. Test: cts-tradefed run commandAndExit cts-dev -m CtsAppSecurityHostTestCases -t android.appsecurity.cts.PermissionsHostTest Test: cts-tradefed run commandAndExit cts-dev -m CtsPermissionTestCases Test: cts-tradefed run commandAndExit cts-dev -m CtsPermission2TestCases Test: bit FrameworksServicesTests:com.android.server.pm.PackageManagerSettingsTests (cherry picked from commit 5f2faaa7ed71a3d1aa862c37e27ebf817f094dc5) --- .../core/java/com/android/server/pm/PackageManagerService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 7be0cde4a94df..7837b029830f6 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -8687,6 +8687,7 @@ public class PackageManagerService extends IPackageManager.Stub pkg.applicationInfo.primaryCpuAbi = updatedPkg.primaryCpuAbiString; pkg.applicationInfo.secondaryCpuAbi = updatedPkg.secondaryCpuAbiString; } + pkg.mExtras = updatedPkg; throw new PackageManagerException(Log.WARN, "Package " + ps.name + " at " + scanFile + " ignored: updated version " + ps.versionCode From 91487cd84b99078f211ad11fcca64fc2dd84e1a2 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 1 Nov 2017 18:39:02 +0000 Subject: [PATCH 010/226] Revert "Fix overactive media routing" This reverts commit 18341301443d0c8e1bd4fc1ba9a7c909593eb918. NullPointerException when converting prevState to int with prevState == null at line 171 in AudioPlayerStateMonitor.java Bug: 68748062 Bug: 65376604 Change-Id: Ib4b457e890a7ee8d9e347df7042dbad5e3018031 (cherry picked from commit 0aec178b7e66a29a22e90415faa15fb5f01aaef2) --- .../server/media/AudioPlaybackMonitor.java | 289 ++++++++++++++++ .../server/media/AudioPlayerStateMonitor.java | 324 ------------------ .../server/media/MediaRouterService.java | 74 ++-- .../server/media/MediaSessionService.java | 28 +- .../server/media/MediaSessionStack.java | 14 +- 5 files changed, 333 insertions(+), 396 deletions(-) create mode 100644 services/core/java/com/android/server/media/AudioPlaybackMonitor.java delete mode 100644 services/core/java/com/android/server/media/AudioPlayerStateMonitor.java diff --git a/services/core/java/com/android/server/media/AudioPlaybackMonitor.java b/services/core/java/com/android/server/media/AudioPlaybackMonitor.java new file mode 100644 index 0000000000000..791ee821b357d --- /dev/null +++ b/services/core/java/com/android/server/media/AudioPlaybackMonitor.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.media; + +import android.content.Context; +import android.media.AudioManager.AudioPlaybackCallback; +import android.media.AudioPlaybackConfiguration; +import android.media.IAudioService; +import android.media.IPlaybackConfigDispatcher; +import android.os.Binder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.IntArray; +import android.util.Log; +import android.util.SparseArray; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Monitors changes in audio playback, and notify the newly started audio playback through the + * {@link OnAudioPlaybackStartedListener} and the activeness change through the + * {@link OnAudioPlaybackActiveStateListener}. + */ +class AudioPlaybackMonitor extends IPlaybackConfigDispatcher.Stub { + private static boolean DEBUG = MediaSessionService.DEBUG; + private static String TAG = "AudioPlaybackMonitor"; + + private static AudioPlaybackMonitor sInstance; + + /** + * Called when audio playback is started for a given UID. + */ + interface OnAudioPlaybackStartedListener { + void onAudioPlaybackStarted(int uid); + } + + /** + * Called when audio player state is changed. + */ + interface OnAudioPlayerActiveStateChangedListener { + void onAudioPlayerActiveStateChanged(int uid, boolean active); + } + + private final Object mLock = new Object(); + private final Context mContext; + private final List mAudioPlaybackStartedListeners + = new ArrayList<>(); + private final List + mAudioPlayerActiveStateChangedListeners = new ArrayList<>(); + private final Map mAudioPlaybackStates = new HashMap<>(); + private final Set mActiveAudioPlaybackClientUids = new HashSet<>(); + + // Sorted array of UIDs that had active audio playback. (i.e. playing an audio/video) + // The UID whose audio playback becomes active at the last comes first. + // TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID. + private final IntArray mSortedAudioPlaybackClientUids = new IntArray(); + + static AudioPlaybackMonitor getInstance(Context context, IAudioService audioService) { + if (sInstance == null) { + sInstance = new AudioPlaybackMonitor(context, audioService); + } + return sInstance; + } + + private AudioPlaybackMonitor(Context context, IAudioService audioService) { + mContext = context; + try { + audioService.registerPlaybackCallback(this); + } catch (RemoteException e) { + Log.wtf(TAG, "Failed to register playback callback", e); + } + } + + /** + * Called when the {@link AudioPlaybackConfiguration} is updated. + *

If an app starts audio playback, the app's local media session will be the media button + * session. If the app has multiple media sessions, the playback active local session will be + * picked. + * + * @param configs List of the current audio playback configuration + */ + @Override + public void dispatchPlaybackConfigChange(List configs, + boolean flush) { + if (flush) { + Binder.flushPendingCommands(); + } + final long token = Binder.clearCallingIdentity(); + try { + List newActiveAudioPlaybackClientUids = new ArrayList<>(); + List audioPlayerActiveStateChangedListeners; + List audioPlaybackStartedListeners; + synchronized (mLock) { + // Update mActiveAudioPlaybackClientUids and mSortedAudioPlaybackClientUids, + // and find newly activated audio playbacks. + mActiveAudioPlaybackClientUids.clear(); + for (AudioPlaybackConfiguration config : configs) { + // Ignore inactive (i.e. not playing) or PLAYER_TYPE_JAM_SOUNDPOOL + // (i.e. playback from the SoundPool class which is only for sound effects) + // playback. + // Note that we shouldn't ignore PLAYER_TYPE_UNKNOWN because it might be OEM + // specific audio/video players. + if (!config.isActive() || config.getPlayerType() + == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) { + continue; + } + + mActiveAudioPlaybackClientUids.add(config.getClientUid()); + Integer oldState = mAudioPlaybackStates.get(config.getPlayerInterfaceId()); + if (!isActiveState(oldState)) { + if (DEBUG) { + Log.d(TAG, "Found a new active media playback. " + + AudioPlaybackConfiguration.toLogFriendlyString(config)); + } + // New active audio playback. + newActiveAudioPlaybackClientUids.add(config.getClientUid()); + int index = mSortedAudioPlaybackClientUids.indexOf(config.getClientUid()); + if (index == 0) { + // It's the lastly played music app already. Skip updating. + continue; + } else if (index > 0) { + mSortedAudioPlaybackClientUids.remove(index); + } + mSortedAudioPlaybackClientUids.add(0, config.getClientUid()); + } + } + audioPlayerActiveStateChangedListeners = new ArrayList<>( + mAudioPlayerActiveStateChangedListeners); + audioPlaybackStartedListeners = new ArrayList<>(mAudioPlaybackStartedListeners); + } + // Notify the change of audio playback states. + for (AudioPlaybackConfiguration config : configs) { + boolean wasActive = isActiveState( + mAudioPlaybackStates.get(config.getPlayerInterfaceId())); + boolean isActive = config.isActive(); + if (wasActive != isActive) { + for (OnAudioPlayerActiveStateChangedListener listener + : audioPlayerActiveStateChangedListeners) { + listener.onAudioPlayerActiveStateChanged(config.getClientUid(), + isActive); + } + } + } + // Notify the start of audio playback + for (int uid : newActiveAudioPlaybackClientUids) { + for (OnAudioPlaybackStartedListener listener : audioPlaybackStartedListeners) { + listener.onAudioPlaybackStarted(uid); + } + } + mAudioPlaybackStates.clear(); + for (AudioPlaybackConfiguration config : configs) { + mAudioPlaybackStates.put(config.getPlayerInterfaceId(), config.getPlayerState()); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Registers OnAudioPlaybackStartedListener. + */ + public void registerOnAudioPlaybackStartedListener(OnAudioPlaybackStartedListener listener) { + synchronized (mLock) { + mAudioPlaybackStartedListeners.add(listener); + } + } + + /** + * Unregisters OnAudioPlaybackStartedListener. + */ + public void unregisterOnAudioPlaybackStartedListener(OnAudioPlaybackStartedListener listener) { + synchronized (mLock) { + mAudioPlaybackStartedListeners.remove(listener); + } + } + + /** + * Registers OnAudioPlayerActiveStateChangedListener. + */ + public void registerOnAudioPlayerActiveStateChangedListener( + OnAudioPlayerActiveStateChangedListener listener) { + synchronized (mLock) { + mAudioPlayerActiveStateChangedListeners.add(listener); + } + } + + /** + * Unregisters OnAudioPlayerActiveStateChangedListener. + */ + public void unregisterOnAudioPlayerActiveStateChangedListener( + OnAudioPlayerActiveStateChangedListener listener) { + synchronized (mLock) { + mAudioPlayerActiveStateChangedListeners.remove(listener); + } + } + + /** + * Returns the sorted list of UIDs that have had active audio playback. (i.e. playing an + * audio/video) The UID whose audio playback becomes active at the last comes first. + */ + public IntArray getSortedAudioPlaybackClientUids() { + IntArray sortedAudioPlaybackClientUids = new IntArray(); + synchronized (mLock) { + sortedAudioPlaybackClientUids.addAll(mSortedAudioPlaybackClientUids); + } + return sortedAudioPlaybackClientUids; + } + + /** + * Returns if the audio playback is active for the uid. + */ + public boolean isPlaybackActive(int uid) { + synchronized (mLock) { + return mActiveAudioPlaybackClientUids.contains(uid); + } + } + + /** + * Cleans up the sorted list of audio playback client UIDs with given {@param + * mediaButtonSessionUid}. + *

UIDs whose audio playback started after the media button session's audio playback + * cannot be the lastly played media app. So they won't needed anymore. + * + * @param mediaButtonSessionUid UID of the media button session. + */ + public void cleanUpAudioPlaybackUids(int mediaButtonSessionUid) { + synchronized (mLock) { + int userId = UserHandle.getUserId(mediaButtonSessionUid); + for (int i = mSortedAudioPlaybackClientUids.size() - 1; i >= 0; i--) { + if (mSortedAudioPlaybackClientUids.get(i) == mediaButtonSessionUid) { + break; + } + int uid = mSortedAudioPlaybackClientUids.get(i); + if (userId == UserHandle.getUserId(uid) && !isPlaybackActive(uid)) { + // Clean up unnecessary UIDs. + // It doesn't need to be managed profile aware because it's just to prevent + // the list from increasing indefinitely. The media button session updating + // shouldn't be affected by cleaning up. + mSortedAudioPlaybackClientUids.remove(i); + } + } + } + } + + /** + * Dumps {@link AudioPlaybackMonitor}. + */ + public void dump(PrintWriter pw, String prefix) { + synchronized (mLock) { + pw.println(prefix + "Audio playback (lastly played comes first)"); + String indent = prefix + " "; + for (int i = 0; i < mSortedAudioPlaybackClientUids.size(); i++) { + int uid = mSortedAudioPlaybackClientUids.get(i); + pw.print(indent + "uid=" + uid + " packages="); + String[] packages = mContext.getPackageManager().getPackagesForUid(uid); + if (packages != null && packages.length > 0) { + for (int j = 0; j < packages.length; j++) { + pw.print(packages[j] + " "); + } + } + pw.println(); + } + } + } + + private boolean isActiveState(Integer state) { + return state != null && state.equals(AudioPlaybackConfiguration.PLAYER_STATE_STARTED); + } +} diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java deleted file mode 100644 index 110f26d5eddc7..0000000000000 --- a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.media; - -import android.annotation.Nullable; -import android.content.Context; -import android.media.AudioPlaybackConfiguration; -import android.media.IAudioService; -import android.media.IPlaybackConfigDispatcher; -import android.os.Binder; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.os.UserHandle; -import android.util.IntArray; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; - -import java.io.PrintWriter; -import java.util.HashSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Monitors the state changes of audio players. - */ -class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub { - private static boolean DEBUG = MediaSessionService.DEBUG; - private static String TAG = "AudioPlayerStateMonitor"; - - private static AudioPlayerStateMonitor sInstance = new AudioPlayerStateMonitor(); - - /** - * Called when the state of audio player is changed. - */ - interface OnAudioPlayerStateChangedListener { - void onAudioPlayerStateChanged( - int uid, int prevState, @Nullable AudioPlaybackConfiguration config); - } - - private final static class MessageHandler extends Handler { - private static final int MSG_AUDIO_PLAYER_STATE_CHANGED = 1; - - private final OnAudioPlayerStateChangedListener mListsner; - - public MessageHandler(Looper looper, OnAudioPlayerStateChangedListener listener) { - super(looper); - mListsner = listener; - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_AUDIO_PLAYER_STATE_CHANGED: - mListsner.onAudioPlayerStateChanged( - msg.arg1, msg.arg2, (AudioPlaybackConfiguration) msg.obj); - break; - } - } - - public void sendAudioPlayerStateChangedMessage(int uid, int prevState, - AudioPlaybackConfiguration config) { - obtainMessage(MSG_AUDIO_PLAYER_STATE_CHANGED, uid, prevState, config).sendToTarget(); - } - } - - private final Object mLock = new Object(); - @GuardedBy("mLock") - private final Map mListenerMap = - new HashMap<>(); - @GuardedBy("mLock") - private final Map mAudioPlayerStates = new HashMap<>(); - @GuardedBy("mLock") - private final Map> mAudioPlayersForUid = new HashMap<>(); - // Sorted array of UIDs that had active audio playback. (i.e. playing an audio/video) - // The UID whose audio playback becomes active at the last comes first. - // TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID. - @GuardedBy("mLock") - private final IntArray mSortedAudioPlaybackClientUids = new IntArray(); - - @GuardedBy("mLock") - private boolean mRegisteredToAudioService; - - static AudioPlayerStateMonitor getInstance() { - return sInstance; - } - - private AudioPlayerStateMonitor() { - } - - /** - * Called when the {@link AudioPlaybackConfiguration} is updated. - *

If an app starts audio playback, the app's local media session will be the media button - * session. If the app has multiple media sessions, the playback active local session will be - * picked. - * - * @param configs List of the current audio playback configuration - */ - @Override - public void dispatchPlaybackConfigChange(List configs, - boolean flush) { - if (flush) { - Binder.flushPendingCommands(); - } - final long token = Binder.clearCallingIdentity(); - try { - final Map prevAudioPlayerStates = new HashMap<>(mAudioPlayerStates); - final Map> prevAudioPlayersForUid = - new HashMap<>(mAudioPlayersForUid); - synchronized (mLock) { - mAudioPlayerStates.clear(); - mAudioPlayersForUid.clear(); - for (AudioPlaybackConfiguration config : configs) { - int pii = config.getPlayerInterfaceId(); - int uid = config.getClientUid(); - mAudioPlayerStates.put(pii, config.getPlayerState()); - HashSet players = mAudioPlayersForUid.get(uid); - if (players == null) { - players = new HashSet(); - players.add(pii); - mAudioPlayersForUid.put(uid, players); - } else { - players.add(pii); - } - } - for (AudioPlaybackConfiguration config : configs) { - if (!config.isActive()) { - continue; - } - - int uid = config.getClientUid(); - if (!isActiveState(prevAudioPlayerStates.get(config.getPlayerInterfaceId()))) { - if (DEBUG) { - Log.d(TAG, "Found a new active media playback. " + - AudioPlaybackConfiguration.toLogFriendlyString(config)); - } - // New active audio playback. - int index = mSortedAudioPlaybackClientUids.indexOf(uid); - if (index == 0) { - // It's the lastly played music app already. Skip updating. - continue; - } else if (index > 0) { - mSortedAudioPlaybackClientUids.remove(index); - } - mSortedAudioPlaybackClientUids.add(0, uid); - } - } - // Notify the change of audio player states. - for (AudioPlaybackConfiguration config : configs) { - Integer prevState = prevAudioPlayerStates.get(config.getPlayerInterfaceId()); - if (prevState == null || prevState != config.getPlayerState()) { - sendAudioPlayerStateChangedMessageLocked( - config.getClientUid(), prevState, config); - } - } - for (Integer prevUid : prevAudioPlayersForUid.keySet()) { - // If all players for prevUid is removed, notify the prev state was - // PLAYER_STATE_STARTED only when there were a player whose state was - // PLAYER_STATE_STARTED, otherwise any inactive state is okay to notify. - if (!mAudioPlayersForUid.containsKey(prevUid)) { - Set players = mAudioPlayersForUid.get(prevUid); - int prevState = AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN; - for (int pii : players) { - Integer state = prevAudioPlayerStates.get(pii); - if (state == null) { - continue; - } - if (state == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { - prevState = state; - break; - } else if (prevState - == AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN) { - prevState = state; - } - } - sendAudioPlayerStateChangedMessageLocked(prevUid, prevState, null); - } - } - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Registers OnAudioPlayerStateChangedListener. - */ - public void registerListener(OnAudioPlayerStateChangedListener listener, Handler handler) { - synchronized (mLock) { - mListenerMap.put(listener, new MessageHandler((handler == null) ? - Looper.myLooper() : handler.getLooper(), listener)); - } - } - - /** - * Unregisters OnAudioPlayerStateChangedListener. - */ - public void unregisterListener(OnAudioPlayerStateChangedListener listener) { - synchronized (mLock) { - mListenerMap.remove(listener); - } - } - - /** - * Returns the sorted list of UIDs that have had active audio playback. (i.e. playing an - * audio/video) The UID whose audio playback becomes active at the last comes first. - */ - public IntArray getSortedAudioPlaybackClientUids() { - IntArray sortedAudioPlaybackClientUids = new IntArray(); - synchronized (mLock) { - sortedAudioPlaybackClientUids.addAll(mSortedAudioPlaybackClientUids); - } - return sortedAudioPlaybackClientUids; - } - - /** - * Returns if the audio playback is active for the uid. - */ - public boolean isPlaybackActive(int uid) { - synchronized (mLock) { - Set players = mAudioPlayersForUid.get(uid); - if (players == null) { - return false; - } - for (Integer pii : players) { - if (isActiveState(mAudioPlayerStates.get(pii))) { - return true; - } - } - return false; - } - } - - /** - * Cleans up the sorted list of audio playback client UIDs with given {@param - * mediaButtonSessionUid}. - *

UIDs whose audio playback are inactive and have started before the media button session's - * audio playback cannot be the lastly played media app. So they won't needed anymore. - * - * @param mediaButtonSessionUid UID of the media button session. - */ - public void cleanUpAudioPlaybackUids(int mediaButtonSessionUid) { - synchronized (mLock) { - int userId = UserHandle.getUserId(mediaButtonSessionUid); - for (int i = mSortedAudioPlaybackClientUids.size() - 1; i >= 0; i--) { - if (mSortedAudioPlaybackClientUids.get(i) == mediaButtonSessionUid) { - break; - } - int uid = mSortedAudioPlaybackClientUids.get(i); - if (userId == UserHandle.getUserId(uid) && !isPlaybackActive(uid)) { - // Clean up unnecessary UIDs. - // It doesn't need to be managed profile aware because it's just to prevent - // the list from increasing indefinitely. The media button session updating - // shouldn't be affected by cleaning up. - mSortedAudioPlaybackClientUids.remove(i); - } - } - } - } - - /** - * Dumps {@link AudioPlayerStateMonitor}. - */ - public void dump(Context context, PrintWriter pw, String prefix) { - synchronized (mLock) { - pw.println(prefix + "Audio playback (lastly played comes first)"); - String indent = prefix + " "; - for (int i = 0; i < mSortedAudioPlaybackClientUids.size(); i++) { - int uid = mSortedAudioPlaybackClientUids.get(i); - pw.print(indent + "uid=" + uid + " packages="); - String[] packages = context.getPackageManager().getPackagesForUid(uid); - if (packages != null && packages.length > 0) { - for (int j = 0; j < packages.length; j++) { - pw.print(packages[j] + " "); - } - } - pw.println(); - } - } - } - - public void registerSelfIntoAudioServiceIfNeeded(IAudioService audioService) { - synchronized (mLock) { - try { - if (!mRegisteredToAudioService) { - audioService.registerPlaybackCallback(this); - mRegisteredToAudioService = true; - } - } catch (RemoteException e) { - Log.wtf(TAG, "Failed to register playback callback", e); - mRegisteredToAudioService = false; - } - } - } - - private void sendAudioPlayerStateChangedMessageLocked( - final int uid, final int prevState, final AudioPlaybackConfiguration config) { - for (MessageHandler messageHandler : mListenerMap.values()) { - messageHandler.sendAudioPlayerStateChangedMessage(uid, prevState, config); - } - } - - private static boolean isActiveState(Integer state) { - return state != null && state.equals(AudioPlaybackConfiguration.PLAYER_STATE_STARTED); - } -} diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index 3c9e1d456c6fd..1cfd5f02e8105 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -19,14 +19,12 @@ package com.android.server.media; import com.android.internal.util.DumpUtils; import com.android.server.Watchdog; -import android.annotation.Nullable; import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.media.AudioPlaybackConfiguration; import android.media.AudioRoutesInfo; import android.media.AudioSystem; import android.media.IAudioRoutesObserver; @@ -98,8 +96,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub private int mCurrentUserId = -1; private boolean mGlobalBluetoothA2dpOn = false; private final IAudioService mAudioService; - private final AudioPlayerStateMonitor mAudioPlayerStateMonitor; - private final Handler mHandler = new Handler(); + private final AudioPlaybackMonitor mAudioPlaybackMonitor; private final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo(); public MediaRouterService(Context context) { @@ -109,57 +106,31 @@ public final class MediaRouterService extends IMediaRouterService.Stub mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); - mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(); - mAudioPlayerStateMonitor.registerListener( - new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() { - static final long WAIT_MS = 500; - final Runnable mRestoreBluetoothA2dpRunnable = new Runnable() { - @Override - public void run() { - restoreBluetoothA2dp(); - } - }; - + mAudioPlaybackMonitor = AudioPlaybackMonitor.getInstance(context, mAudioService); + mAudioPlaybackMonitor.registerOnAudioPlayerActiveStateChangedListener( + new AudioPlaybackMonitor.OnAudioPlayerActiveStateChangedListener() { @Override - public void onAudioPlayerStateChanged( - int uid, int prevState, @Nullable AudioPlaybackConfiguration config) { - int restoreUid = -1; - boolean active = config == null ? false : config.isActive(); + public void onAudioPlayerActiveStateChanged(int uid, boolean active) { if (active) { - restoreUid = uid; - } else if (prevState != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { - // Noting to do if the prev state is not an active state. - return; + restoreRoute(uid); } else { IntArray sortedAudioPlaybackClientUids = - mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids(); - for (int i = 0; i < sortedAudioPlaybackClientUids.size(); ++i) { - if (mAudioPlayerStateMonitor.isPlaybackActive( + mAudioPlaybackMonitor.getSortedAudioPlaybackClientUids(); + boolean restored = false; + for (int i = 0; i < sortedAudioPlaybackClientUids.size(); i++) { + if (mAudioPlaybackMonitor.isPlaybackActive( sortedAudioPlaybackClientUids.get(i))) { - restoreUid = sortedAudioPlaybackClientUids.get(i); + restoreRoute(sortedAudioPlaybackClientUids.get(i)); + restored = true; break; } } - } - - mHandler.removeCallbacks(mRestoreBluetoothA2dpRunnable); - if (restoreUid >= 0) { - restoreRoute(restoreUid); - if (DEBUG) { - Slog.d(TAG, "onAudioPlayerStateChanged: " + "uid " + uid - + " active " + active + " restoring " + restoreUid); - } - } else { - mHandler.postDelayed(mRestoreBluetoothA2dpRunnable, WAIT_MS); - if (DEBUG) { - Slog.d(TAG, "onAudioPlayerStateChanged: " + "uid " + uid - + " active " + active + " delaying"); + if (!restored) { + restoreBluetoothA2dp(); } } } - }, mHandler); - mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService); - + }); AudioRoutesInfo audioRoutes = null; try { audioRoutes = mAudioService.startWatchingRoutes(new IAudioRoutesObserver.Stub() { @@ -290,14 +261,9 @@ public final class MediaRouterService extends IMediaRouterService.Stub final long token = Binder.clearCallingIdentity(); try { - ClientRecord clientRecord; synchronized (mLock) { - clientRecord = mAllClientRecords.get(client.asBinder()); + return isPlaybackActiveLocked(client); } - if (clientRecord != null) { - return mAudioPlayerStateMonitor.isPlaybackActive(clientRecord.mUid); - } - return false; } finally { Binder.restoreCallingIdentity(token); } @@ -514,6 +480,14 @@ public final class MediaRouterService extends IMediaRouterService.Stub return null; } + private boolean isPlaybackActiveLocked(IMediaRouterClient client) { + ClientRecord clientRecord = mAllClientRecords.get(client.asBinder()); + if (clientRecord != null) { + return mAudioPlaybackMonitor.isPlaybackActive(clientRecord.mUid); + } + return false; + } + private void setDiscoveryRequestLocked(IMediaRouterClient client, int routeTypes, boolean activeScan) { final IBinder binder = client.asBinder(); diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index f6a81d07277a5..aa652445c45a1 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -16,7 +16,6 @@ package com.android.server.media; -import android.annotation.Nullable; import android.app.ActivityManager; import android.app.INotificationManager; import android.app.KeyguardManager; @@ -32,7 +31,7 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.media.AudioManager; -import android.media.AudioPlaybackConfiguration; +import android.media.AudioManagerInternal; import android.media.AudioSystem; import android.media.IAudioService; import android.media.IRemoteVolumeController; @@ -69,6 +68,7 @@ import android.view.KeyEvent; import android.view.ViewConfiguration; import com.android.internal.util.DumpUtils; +import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.Watchdog; import com.android.server.Watchdog.Monitor; @@ -104,6 +104,7 @@ public class MediaSessionService extends SystemService implements Monitor { private KeyguardManager mKeyguardManager; private IAudioService mAudioService; + private AudioManagerInternal mAudioManagerInternal; private ContentResolver mContentResolver; private SettingsObserver mSettingsObserver; private INotificationManager mNotificationManager; @@ -113,7 +114,7 @@ public class MediaSessionService extends SystemService implements Monitor { // It's always not null after the MediaSessionService is started. private FullUserRecord mCurrentFullUserRecord; private MediaSessionRecord mGlobalPrioritySession; - private AudioPlayerStateMonitor mAudioPlayerStateMonitor; + private AudioPlaybackMonitor mAudioPlaybackMonitor; // Used to notify system UI when remote volume was changed. TODO find a // better way to handle this. @@ -136,16 +137,11 @@ public class MediaSessionService extends SystemService implements Monitor { mKeyguardManager = (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE); mAudioService = getAudioService(); - mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(); - mAudioPlayerStateMonitor.registerListener( - new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() { + mAudioPlaybackMonitor = AudioPlaybackMonitor.getInstance(getContext(), mAudioService); + mAudioPlaybackMonitor.registerOnAudioPlaybackStartedListener( + new AudioPlaybackMonitor.OnAudioPlaybackStartedListener() { @Override - public void onAudioPlayerStateChanged( - int uid, int prevState, @Nullable AudioPlaybackConfiguration config) { - if (config == null || !config.isActive() || config.getPlayerType() - == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) { - return; - } + public void onAudioPlaybackStarted(int uid) { synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(UserHandle.getUserId(uid)); @@ -154,8 +150,8 @@ public class MediaSessionService extends SystemService implements Monitor { } } } - }, null /* handler */); - mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService); + }); + mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class); mContentResolver = getContext().getContentResolver(); mSettingsObserver = new SettingsObserver(); mSettingsObserver.observe(); @@ -654,7 +650,7 @@ public class MediaSessionService extends SystemService implements Monitor { public FullUserRecord(int fullUserId) { mFullUserId = fullUserId; - mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this); + mPriorityStack = new MediaSessionStack(mAudioPlaybackMonitor, this); // Restore the remembered media button receiver before the boot. String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId); @@ -1313,7 +1309,7 @@ public class MediaSessionService extends SystemService implements Monitor { for (int i = 0; i < count; i++) { mUserRecords.valueAt(i).dumpLocked(pw, ""); } - mAudioPlayerStateMonitor.dump(getContext(), pw, ""); + mAudioPlaybackMonitor.dump(pw, ""); } } diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index 719ec362e6e8c..d9fe72e0c8bbe 100644 --- a/services/core/java/com/android/server/media/MediaSessionStack.java +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -75,7 +75,7 @@ class MediaSessionStack { */ private final List mSessions = new ArrayList(); - private final AudioPlayerStateMonitor mAudioPlayerStateMonitor; + private final AudioPlaybackMonitor mAudioPlaybackMonitor; private final OnMediaButtonSessionChangedListener mOnMediaButtonSessionChangedListener; /** @@ -84,6 +84,7 @@ class MediaSessionStack { */ private MediaSessionRecord mMediaButtonSession; + private MediaSessionRecord mCachedDefault; private MediaSessionRecord mCachedVolumeDefault; /** @@ -92,8 +93,8 @@ class MediaSessionStack { private final SparseArray> mCachedActiveLists = new SparseArray<>(); - MediaSessionStack(AudioPlayerStateMonitor monitor, OnMediaButtonSessionChangedListener listener) { - mAudioPlayerStateMonitor = monitor; + MediaSessionStack(AudioPlaybackMonitor monitor, OnMediaButtonSessionChangedListener listener) { + mAudioPlaybackMonitor = monitor; mOnMediaButtonSessionChangedListener = listener; } @@ -186,13 +187,13 @@ class MediaSessionStack { if (DEBUG) { Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + Debug.getCallers(2)); } - IntArray audioPlaybackUids = mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids(); + IntArray audioPlaybackUids = mAudioPlaybackMonitor.getSortedAudioPlaybackClientUids(); for (int i = 0; i < audioPlaybackUids.size(); i++) { MediaSessionRecord mediaButtonSession = findMediaButtonSession(audioPlaybackUids.get(i)); if (mediaButtonSession != null) { // Found the media button session. - mAudioPlayerStateMonitor.cleanUpAudioPlaybackUids(mediaButtonSession.getUid()); + mAudioPlaybackMonitor.cleanUpAudioPlaybackUids(mediaButtonSession.getUid()); if (mMediaButtonSession != mediaButtonSession) { updateMediaButtonSession(mediaButtonSession); } @@ -215,7 +216,7 @@ class MediaSessionStack { for (MediaSessionRecord session : mSessions) { if (uid == session.getUid()) { if (session.getPlaybackState() != null && session.isPlaybackActive() == - mAudioPlayerStateMonitor.isPlaybackActive(session.getUid())) { + mAudioPlaybackMonitor.isPlaybackActive(session.getUid())) { // If there's a media session whose PlaybackState matches // the audio playback state, return it immediately. return session; @@ -375,6 +376,7 @@ class MediaSessionStack { } private void clearCache(int userId) { + mCachedDefault = null; mCachedVolumeDefault = null; mCachedActiveLists.remove(userId); // mCachedActiveLists may also include the list of sessions for UserHandle.USER_ALL, From 41044af186c7d954b02c2fea15d2c2988cd41f45 Mon Sep 17 00:00:00 2001 From: Charles He Date: Thu, 2 Nov 2017 16:16:12 +0000 Subject: [PATCH 011/226] Revert "Suppress immersive mode cling in LockTask mode." This reverts commit 29fef4329c846a4d593e2abd06c2c42596bdb1a9. We shouldn't call into AM with WM lock held. Bug: 68795262 Bug: 68305547 Change-Id: I4723488df4c5fa6839fd00c05bf3eb61253185f1 (cherry picked from commit 009050b505951edf83fcb4fd3e7e30ce780645ed) --- .../server/policy/ImmersiveModeConfirmation.java | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java index edd2fdb1b33a3..c6ec287d9c6a5 100644 --- a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java +++ b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java @@ -52,6 +52,7 @@ import android.widget.Button; import android.widget.FrameLayout; import com.android.internal.R; +import com.android.server.vr.VrManagerService; /** * Helper to manage showing/hiding a confirmation prompt when the navigation bar is hidden @@ -147,7 +148,6 @@ public class ImmersiveModeConfirmation { && userSetupComplete && !mVrModeEnabled && !navBarEmpty - && !isLockTaskModeLocked() && !UserManager.isDeviceInDemoMode(mContext)) { mHandler.sendEmptyMessageDelayed(H.SHOW, mShowDelayMs); } @@ -156,20 +156,6 @@ public class ImmersiveModeConfirmation { } } - /** - * @return {@code true} if and only if the device is currently in LockTask mode managed by - * {@link android.app.admin.DevicePolicyManager}. Note that this differs from the screen pinning - * mode which is initiated by the user. - */ - private boolean isLockTaskModeLocked() { - try { - return ActivityManager.getService().getLockTaskModeState() - == ActivityManager.LOCK_TASK_MODE_LOCKED; - } catch (RemoteException e) { - return false; - } - } - public boolean onPowerKeyDown(boolean isScreenOn, long time, boolean inImmersiveMode, boolean navBarEmpty) { if (!isScreenOn && (time - mPanicTime < mPanicThresholdMs)) { From d9f3994354e7a5db9fe5376d62a6f9ba499a4f7f Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 2 Nov 2017 15:57:28 -0700 Subject: [PATCH 012/226] Ensure closeSystemDialogs() is not oneway. Bug: 68787785 Test: Launch overview with QS down Change-Id: I136a3d2e45a91abe364027747bca03d4edb454c5 (cherry picked from commit 84deaa29d4f72f66b645df12048a98a83aaa3fac) --- core/java/android/app/IActivityManager.aidl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 388459fbbc6e1..bbc90c81fae8e 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -232,7 +232,7 @@ interface IActivityManager { boolean requireFull, in String name, in String callerPackage); void addPackageDependency(in String packageName); void killApplication(in String pkg, int appId, int userId, in String reason); - oneway void closeSystemDialogs(in String reason); + void closeSystemDialogs(in String reason); Debug.MemoryInfo[] getProcessMemoryInfo(in int[] pids); void killApplicationProcess(in String processName, int uid); int startActivityIntentSender(in IApplicationThread caller, From a85e626da8ab27fa942bbd06bcf5f02878a066af Mon Sep 17 00:00:00 2001 From: Matthew Ng Date: Tue, 7 Nov 2017 11:50:36 -0800 Subject: [PATCH 013/226] Fixes service disconnect crash when changing users Changing users crashes system ui because service disconnected from unprovisioned user that had no connection. Fix by always disconnecting if connected before trying to connect to service. Requires follow up change to have new provisioned user to connect to service. Change-Id: I350ec45b90fa297c6ffe4932325c3b07653538f3 Fixes: 68929438 Test: manual (cherry picked from commit 1fa3f7eebc0deb8170a1a57422af2e9cb8f9bafd) --- .../com/android/systemui/OverviewProxyService.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java index 3878cd1cf9263..4194701327f00 100644 --- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java @@ -78,7 +78,6 @@ public class OverviewProxyService { @Override public void onUserSwitched() { - disconnectFromLauncherService(); mConnectionBackoffAttempts = 0; startConnectionToCurrentUser(); } @@ -100,8 +99,10 @@ public class OverviewProxyService { } public void startConnectionToCurrentUser() { + disconnectFromLauncherService(); + // If user has not setup yet or already connected, do not try to connect - if (!mDeviceProvisionedController.isCurrentUserSetup() || mOverviewProxy != null) { + if (!mDeviceProvisionedController.isCurrentUserSetup()) { return; } mHandler.removeCallbacks(mConnectionRunnable); @@ -124,7 +125,9 @@ public class OverviewProxyService { } private void disconnectFromLauncherService() { - mContext.unbindService(mOverviewServiceConnection); - mOverviewProxy = null; + if (mOverviewProxy != null) { + mContext.unbindService(mOverviewServiceConnection); + mOverviewProxy = null; + } } } From 7542ccd40c41c3acdbe911f0d0339cace1d15a69 Mon Sep 17 00:00:00 2001 From: Evan Rosky Date: Thu, 9 Nov 2017 19:25:24 +0000 Subject: [PATCH 014/226] Revert "Enable new initial-focus behavior for P" This reverts commit 4dc3289c942398507a8b6240559da599d11a3284. Reason for revert: Pushback from bundled apps. Bug: 68841055 Bug: 34520588 Change-Id: Id771a11c9b44baf5810240cca164bae9f468b5ad (cherry picked from commit b34cad00d374e09eed4df028dee5f82ea32a07d7) --- core/java/android/view/ViewRootImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 37829f0bf2214..e30496fb718bb 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -513,7 +513,7 @@ public final class ViewRootImpl implements ViewParent, mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); if (!sCompatibilityDone) { - sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P; + sAlwaysAssignFocus = true; sCompatibilityDone = true; } From f90cbb6b47ac3d0bb890ea9e829054446ef2b7da Mon Sep 17 00:00:00 2001 From: Wale Ogunwale Date: Mon, 13 Nov 2017 16:49:19 +0000 Subject: [PATCH 015/226] Revert "Support insets on secondary displays" This reverts commit 9bc2692939d4dececbfde5ea67df4594a7842bd1. Change-Id: Ia065372e9e220e653b4729a290663c3536d99307 Fixes: 69241956 Bug: 64148922 (cherry picked from commit 7bb06e012a33c94c68b173bc8f276f78e0d60764) --- api/current.txt | 1 - api/system-current.txt | 1 - api/test-current.txt | 1 - core/java/android/app/Instrumentation.java | 44 +- core/java/android/view/DisplayFrames.java | 189 --- .../android/view/WindowManagerPolicy.java | 62 +- .../android/server/windowmanagerservice.proto | 6 +- graphics/java/android/graphics/Rect.java | 13 - .../server/policy/PhoneWindowManager.java | 1212 +++++++++++------ .../com/android/server/wm/DisplayContent.java | 39 +- .../server/wm/RootWindowContainer.java | 2 +- .../server/wm/WindowManagerService.java | 49 +- .../server/wm/ScreenDecorWindowTests.java | 80 +- .../server/wm/TestWindowManagerPolicy.java | 39 + 14 files changed, 938 insertions(+), 800 deletions(-) delete mode 100644 core/java/android/view/DisplayFrames.java diff --git a/api/current.txt b/api/current.txt index cebd6d6a6b8c7..9abc0bed553c0 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4943,7 +4943,6 @@ package android.app { method public void setInTouchMode(boolean); method public void start(); method public android.app.Activity startActivitySync(android.content.Intent); - method public android.app.Activity startActivitySync(android.content.Intent, android.os.Bundle); method public deprecated void startAllocCounting(); method public void startPerformanceSnapshot(); method public void startProfiling(); diff --git a/api/system-current.txt b/api/system-current.txt index e1f22724b2600..18b6f5bde2cfb 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5129,7 +5129,6 @@ package android.app { method public void setInTouchMode(boolean); method public void start(); method public android.app.Activity startActivitySync(android.content.Intent); - method public android.app.Activity startActivitySync(android.content.Intent, android.os.Bundle); method public deprecated void startAllocCounting(); method public void startPerformanceSnapshot(); method public void startProfiling(); diff --git a/api/test-current.txt b/api/test-current.txt index 92b41a8dbdc83..d2c24d5ce2f01 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -4972,7 +4972,6 @@ package android.app { method public void setInTouchMode(boolean); method public void start(); method public android.app.Activity startActivitySync(android.content.Intent); - method public android.app.Activity startActivitySync(android.content.Intent, android.os.Bundle); method public deprecated void startAllocCounting(); method public void startPerformanceSnapshot(); method public void startProfiling(); diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index d49e11f47fea1..e260967f92d07 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -17,7 +17,6 @@ package android.app; import android.annotation.IntDef; -import android.annotation.Nullable; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; @@ -419,51 +418,22 @@ public class Instrumentation { * different process. In addition, if the given Intent resolves to * multiple activities, instead of displaying a dialog for the user to * select an activity, an exception will be thrown. - * + * *

The function returns as soon as the activity goes idle following the * call to its {@link Activity#onCreate}. Generally this means it has gone * through the full initialization including {@link Activity#onResume} and * drawn and displayed its initial window. - * + * * @param intent Description of the activity to start. - * + * * @see Context#startActivity - * @see #startActivitySync(Intent, Bundle) */ public Activity startActivitySync(Intent intent) { - return startActivitySync(intent, null /* options */); - } - - /** - * Start a new activity and wait for it to begin running before returning. - * In addition to being synchronous, this method as some semantic - * differences from the standard {@link Context#startActivity} call: the - * activity component is resolved before talking with the activity manager - * (its class name is specified in the Intent that this method ultimately - * starts), and it does not allow you to start activities that run in a - * different process. In addition, if the given Intent resolves to - * multiple activities, instead of displaying a dialog for the user to - * select an activity, an exception will be thrown. - * - *

The function returns as soon as the activity goes idle following the - * call to its {@link Activity#onCreate}. Generally this means it has gone - * through the full initialization including {@link Activity#onResume} and - * drawn and displayed its initial window. - * - * @param intent Description of the activity to start. - * @param options Additional options for how the Activity should be started. - * May be null if there are no options. See {@link android.app.ActivityOptions} - * for how to build the Bundle supplied here; there are no supported definitions - * for building it manually. - * - * @see Context#startActivity(Intent, Bundle) - */ - public Activity startActivitySync(Intent intent, @Nullable Bundle options) { validateNotAppThread(); synchronized (mSync) { intent = new Intent(intent); - + ActivityInfo ai = intent.resolveActivityInfo( getTargetContext().getPackageManager(), 0); if (ai == null) { @@ -477,7 +447,7 @@ public class Instrumentation { + myProc + " resolved to different process " + ai.processName + ": " + intent); } - + intent.setComponent(new ComponentName( ai.applicationInfo.packageName, ai.name)); final ActivityWaiter aw = new ActivityWaiter(intent); @@ -487,7 +457,7 @@ public class Instrumentation { } mWaitingActivities.add(aw); - getTargetContext().startActivity(intent, options); + getTargetContext().startActivity(intent); do { try { @@ -495,7 +465,7 @@ public class Instrumentation { } catch (InterruptedException e) { } } while (mWaitingActivities.contains(aw)); - + return aw.activity; } } diff --git a/core/java/android/view/DisplayFrames.java b/core/java/android/view/DisplayFrames.java deleted file mode 100644 index e6861d83d2fd9..0000000000000 --- a/core/java/android/view/DisplayFrames.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package android.view; - -import static android.view.Surface.ROTATION_180; -import static android.view.Surface.ROTATION_270; -import static android.view.Surface.ROTATION_90; -import static com.android.server.wm.proto.DisplayFramesProto.STABLE_BOUNDS; - -import android.graphics.Rect; -import android.util.proto.ProtoOutputStream; - -import java.io.PrintWriter; - -/** - * Container class for all the display frames that affect how we do window layout on a display. - * @hide - */ -public class DisplayFrames { - public final int mDisplayId; - - /** - * The current size of the screen; really; extends into the overscan area of the screen and - * doesn't account for any system elements like the status bar. - */ - public final Rect mOverscan = new Rect(); - - /** - * The current visible size of the screen; really; (ir)regardless of whether the status bar can - * be hidden but not extending into the overscan area. - */ - public final Rect mUnrestricted = new Rect(); - - /** Like mOverscan*, but allowed to move into the overscan region where appropriate. */ - public final Rect mRestrictedOverscan = new Rect(); - - /** - * The current size of the screen; these may be different than (0,0)-(dw,dh) if the status bar - * can't be hidden; in that case it effectively carves out that area of the display from all - * other windows. - */ - public final Rect mRestricted = new Rect(); - - /** - * During layout, the current screen borders accounting for any currently visible system UI - * elements. - */ - public final Rect mSystem = new Rect(); - - /** For applications requesting stable content insets, these are them. */ - public final Rect mStable = new Rect(); - - /** - * For applications requesting stable content insets but have also set the fullscreen window - * flag, these are the stable dimensions without the status bar. - */ - public final Rect mStableFullscreen = new Rect(); - - /** - * During layout, the current screen borders with all outer decoration (status bar, input method - * dock) accounted for. - */ - public final Rect mCurrent = new Rect(); - - /** - * During layout, the frame in which content should be displayed to the user, accounting for all - * screen decoration except for any space they deem as available for other content. This is - * usually the same as mCurrent*, but may be larger if the screen decor has supplied content - * insets. - */ - public final Rect mContent = new Rect(); - - /** - * During layout, the frame in which voice content should be displayed to the user, accounting - * for all screen decoration except for any space they deem as available for other content. - */ - public final Rect mVoiceContent = new Rect(); - - /** During layout, the current screen borders along which input method windows are placed. */ - public final Rect mDock = new Rect(); - - private final Rect mDisplayInfoOverscan = new Rect(); - private final Rect mRotatedDisplayInfoOverscan = new Rect(); - public int mDisplayWidth; - public int mDisplayHeight; - - public int mRotation; - - public DisplayFrames(int displayId, DisplayInfo info) { - mDisplayId = displayId; - onDisplayInfoUpdated(info); - } - - public void onDisplayInfoUpdated(DisplayInfo info) { - mDisplayWidth = info.logicalWidth; - mDisplayHeight = info.logicalHeight; - mRotation = info.rotation; - mDisplayInfoOverscan.set( - info.overscanLeft, info.overscanTop, info.overscanRight, info.overscanBottom); - } - - public void onBeginLayout() { - switch (mRotation) { - case ROTATION_90: - mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.top; - mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.right; - mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.bottom; - mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.left; - break; - case ROTATION_180: - mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.right; - mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.bottom; - mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.left; - mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.top; - break; - case ROTATION_270: - mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.bottom; - mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.left; - mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.top; - mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.right; - break; - default: - mRotatedDisplayInfoOverscan.set(mDisplayInfoOverscan); - break; - } - - mRestrictedOverscan.set(0, 0, mDisplayWidth, mDisplayHeight); - mOverscan.set(mRestrictedOverscan); - mSystem.set(mRestrictedOverscan); - mUnrestricted.set(mRotatedDisplayInfoOverscan); - mUnrestricted.right = mDisplayWidth - mUnrestricted.right; - mUnrestricted.bottom = mDisplayHeight - mUnrestricted.bottom; - mRestricted.set(mUnrestricted); - mDock.set(mUnrestricted); - mContent.set(mUnrestricted); - mVoiceContent.set(mUnrestricted); - mStable.set(mUnrestricted); - mStableFullscreen.set(mUnrestricted); - mCurrent.set(mUnrestricted); - - } - - public int getInputMethodWindowVisibleHeight() { - return mDock.bottom - mCurrent.bottom; - } - - public void writeToProto(ProtoOutputStream proto, long fieldId) { - final long token = proto.start(fieldId); - mStable.writeToProto(proto, STABLE_BOUNDS); - proto.end(token); - } - - public void dump(String prefix, PrintWriter pw) { - pw.println(prefix + "DisplayFrames w=" + mDisplayWidth + " h=" + mDisplayHeight - + " r=" + mRotation); - final String myPrefix = prefix + " "; - dumpFrame(mStable, "mStable", myPrefix, pw); - dumpFrame(mStableFullscreen, "mStableFullscreen", myPrefix, pw); - dumpFrame(mDock, "mDock", myPrefix, pw); - dumpFrame(mCurrent, "mCurrent", myPrefix, pw); - dumpFrame(mSystem, "mSystem", myPrefix, pw); - dumpFrame(mContent, "mContent", myPrefix, pw); - dumpFrame(mVoiceContent, "mVoiceContent", myPrefix, pw); - dumpFrame(mOverscan, "mOverscan", myPrefix, pw); - dumpFrame(mRestrictedOverscan, "mRestrictedOverscan", myPrefix, pw); - dumpFrame(mRestricted, "mRestricted", myPrefix, pw); - dumpFrame(mUnrestricted, "mUnrestricted", myPrefix, pw); - dumpFrame(mDisplayInfoOverscan, "mDisplayInfoOverscan", myPrefix, pw); - dumpFrame(mRotatedDisplayInfoOverscan, "mRotatedDisplayInfoOverscan", myPrefix, pw); - } - - private void dumpFrame(Rect frame, String name, String prefix, PrintWriter pw) { - pw.print(prefix + name + "="); frame.printShortString(pw); pw.println(); - } -} diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 534335bf4743e..ebe3633de402c 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -66,6 +66,7 @@ import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType; import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.app.ActivityManager.StackId; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.CompatibilityInfo; @@ -720,6 +721,12 @@ public interface WindowManagerPolicy { */ public void setInitialDisplaySize(Display display, int width, int height, int density); + /** + * Called by window manager to set the overscan region that should be used for the + * given display. + */ + public void setDisplayOverscan(Display display, int left, int top, int right, int bottom); + /** * Check permissions when adding a window. * @@ -1166,10 +1173,14 @@ public interface WindowManagerPolicy { /** * Called when layout of the windows is about to start. * - * @param displayFrames frames of the display we are doing layout on. + * @param displayId Id of the display we are doing layout on. + * @param displayWidth The current full width of the screen. + * @param displayHeight The current full height of the screen. + * @param displayRotation The current rotation being applied to the base window. * @param uiMode The current uiMode in configuration. */ - default void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {} + public void beginLayoutLw(int displayId, int displayWidth, int displayHeight, + int displayRotation, int uiMode); /** * Returns the bottom-most layer of the system decor, above which no policy decor should @@ -1178,28 +1189,37 @@ public interface WindowManagerPolicy { public int getSystemDecorLayerLw(); /** - * Called for each window attached to the window manager as layout is proceeding. The - * implementation of this function must take care of setting the window's frame, either here or - * in finishLayout(). + * Return the rectangle of the screen that is available for applications to run in. + * This will be called immediately after {@link #beginLayoutLw}. + * + * @param r The rectangle to be filled with the boundaries available to applications. + */ + public void getContentRectLw(Rect r); + + /** + * Called for each window attached to the window manager as layout is + * proceeding. The implementation of this function must take care of + * setting the window's frame, either here or in finishLayout(). * * @param win The window being positioned. * @param attached For sub-windows, the window it is attached to; this * window will already have had layoutWindow() called on it * so you can use its Rect. Otherwise null. - * @param displayFrames The display frames. */ - default void layoutWindowLw( - WindowState win, WindowState attached, DisplayFrames displayFrames) {} + public void layoutWindowLw(WindowState win, WindowState attached); /** - * Return the insets for the areas covered by system windows. These values are computed on the - * most recent layout, so they are not guaranteed to be correct. + * Return the insets for the areas covered by system windows. These values + * are computed on the most recent layout, so they are not guaranteed to + * be correct. * * @param attrs The LayoutParams of the window. * @param taskBounds The bounds of the task this window is on or {@code null} if no task is * associated with the window. - * @param displayFrames display frames. + * @param displayRotation Rotation of the display. + * @param displayWidth The width of the display. + * @param displayHeight The height of the display. * @param outContentInsets The areas covered by system windows, expressed as positive insets. * @param outStableInsets The areas covered by stable system windows irrespective of their * current visibility. Expressed as positive insets. @@ -1207,11 +1227,16 @@ public interface WindowManagerPolicy { * @return Whether to always consume the navigation bar. * See {@link #isNavBarForcedShownLw(WindowState)}. */ - default boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds, - DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets, - Rect outOutsets) { - return false; - } + public boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds, + int displayRotation, int displayWidth, int displayHeight, Rect outContentInsets, + Rect outStableInsets, Rect outOutsets); + + /** + * Called when layout of the windows is finished. After this function has + * returned, all windows given to layoutWindow() must have had a + * frame assigned. + */ + public void finishLayoutLw(); /** Layout state may have changed (so another layout will be performed) */ static final int FINISH_LAYOUT_REDO_LAYOUT = 0x0001; @@ -1627,6 +1652,11 @@ public interface WindowManagerPolicy { */ public void showGlobalActions(); + /** + * @return The current height of the input method window. + */ + public int getInputMethodWindowVisibleHeightLw(); + /** * Called when the current user changes. Guaranteed to be called before the broadcast * of the new user id is made to all listeners. diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index 0228edb03a83a..4d48a42992813 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -48,6 +48,7 @@ message RootWindowContainerProto { /* represents PhoneWindowManager */ message WindowManagerPolicyProto { + optional .android.graphics.RectProto stable_bounds = 1; } /* represents AppTransition */ @@ -100,13 +101,8 @@ message DisplayProto { optional .android.view.DisplayInfoProto display_info = 10; optional int32 rotation = 11; optional ScreenRotationAnimationProto screen_rotation_animation = 12; - optional DisplayFramesProto display_frames = 13; } -/* represents DisplayFrames */ -message DisplayFramesProto { - optional .android.graphics.RectProto stable_bounds = 1; -} /* represents DockedStackDividerController */ message DockedStackDividerControllerProto { diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java index aff942da78d12..3dc928de60df8 100644 --- a/graphics/java/android/graphics/Rect.java +++ b/graphics/java/android/graphics/Rect.java @@ -474,19 +474,6 @@ public final class Rect implements Parcelable { return intersect(r.left, r.top, r.right, r.bottom); } - /** - * If the specified rectangle intersects this rectangle, set this rectangle to that - * intersection, otherwise set this rectangle to the empty rectangle. - * @see #inset(int, int, int, int) but without checking if the rects overlap. - * @hide - */ - public void intersectUnchecked(Rect other) { - left = Math.max(left, other.left); - top = Math.max(top, other.top); - right = Math.min(right, other.right); - bottom = Math.min(bottom, other.bottom); - } - /** * If rectangles a and b intersect, return true and set this rectangle to * that intersection, otherwise return false and do not change this diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index a11d28279840e..7837940434aa0 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -59,8 +59,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; -import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; -import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW; @@ -68,7 +66,6 @@ import static android.view.WindowManager.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; @@ -126,8 +123,11 @@ import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT; import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED; import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN; +import static com.android.server.wm.proto.WindowManagerPolicyProto.STABLE_BOUNDS; + import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityManager.StackId; import android.app.ActivityManagerInternal; import android.app.ActivityManagerInternal.SleepToken; import android.app.ActivityThread; @@ -210,7 +210,6 @@ import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import android.view.Display; -import android.view.DisplayFrames; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.IApplicationToken; @@ -597,6 +596,47 @@ public class PhoneWindowManager implements WindowManagerPolicy { PointerLocationView mPointerLocationView; + // The current size of the screen; really; extends into the overscan area of + // the screen and doesn't account for any system elements like the status bar. + int mOverscanScreenLeft, mOverscanScreenTop; + int mOverscanScreenWidth, mOverscanScreenHeight; + // The current visible size of the screen; really; (ir)regardless of whether the status + // bar can be hidden but not extending into the overscan area. + int mUnrestrictedScreenLeft, mUnrestrictedScreenTop; + int mUnrestrictedScreenWidth, mUnrestrictedScreenHeight; + // Like mOverscanScreen*, but allowed to move into the overscan region where appropriate. + int mRestrictedOverscanScreenLeft, mRestrictedOverscanScreenTop; + int mRestrictedOverscanScreenWidth, mRestrictedOverscanScreenHeight; + // The current size of the screen; these may be different than (0,0)-(dw,dh) + // if the status bar can't be hidden; in that case it effectively carves out + // that area of the display from all other windows. + int mRestrictedScreenLeft, mRestrictedScreenTop; + int mRestrictedScreenWidth, mRestrictedScreenHeight; + // During layout, the current screen borders accounting for any currently + // visible system UI elements. + int mSystemLeft, mSystemTop, mSystemRight, mSystemBottom; + // For applications requesting stable content insets, these are them. + int mStableLeft, mStableTop, mStableRight, mStableBottom; + // For applications requesting stable content insets but have also set the + // fullscreen window flag, these are the stable dimensions without the status bar. + int mStableFullscreenLeft, mStableFullscreenTop; + int mStableFullscreenRight, mStableFullscreenBottom; + // During layout, the current screen borders with all outer decoration + // (status bar, input method dock) accounted for. + int mCurLeft, mCurTop, mCurRight, mCurBottom; + // During layout, the frame in which content should be displayed + // to the user, accounting for all screen decoration except for any + // space they deem as available for other content. This is usually + // the same as mCur*, but may be larger if the screen decor has supplied + // content insets. + int mContentLeft, mContentTop, mContentRight, mContentBottom; + // During layout, the frame in which voice content should be displayed + // to the user, accounting for all screen decoration except for any + // space they deem as available for other content. + int mVoiceContentLeft, mVoiceContentTop, mVoiceContentRight, mVoiceContentBottom; + // During layout, the current screen borders along which input method + // windows are placed. + int mDockLeft, mDockTop, mDockRight, mDockBottom; // During layout, the layer at which the doc window is placed. int mDockLayer; // During layout, this is the layer of the status bar. @@ -694,11 +734,18 @@ public class PhoneWindowManager implements WindowManagerPolicy { Display mDisplay; + private int mDisplayRotation; + int mLandscapeRotation = 0; // default landscape rotation int mSeascapeRotation = 0; // "other" landscape rotation, 180 degrees from mLandscapeRotation int mPortraitRotation = 0; // default portrait rotation int mUpsideDownRotation = 0; // "other" portrait rotation + int mOverscanLeft = 0; + int mOverscanTop = 0; + int mOverscanRight = 0; + int mOverscanBottom = 0; + // What we do when the user long presses on home private int mLongPressOnHomeBehavior; @@ -1014,7 +1061,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { View.NAVIGATION_BAR_UNHIDE, View.NAVIGATION_BAR_TRANSLUCENT, StatusBarManager.WINDOW_NAVIGATION_BAR, - FLAG_TRANSLUCENT_NAVIGATION, + WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, View.NAVIGATION_BAR_TRANSPARENT); private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener = @@ -2021,7 +2068,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { context.registerReceiver(mMultiuserReceiver, filter); // monitor for system gestures - // TODO(multi-display): Needs to be display specific. mSystemGestures = new SystemGesturesPointerEventListener(context, new SystemGesturesPointerEventListener.Callbacks() { @Override @@ -2268,6 +2314,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { return mForceDefaultOrientation; } + @Override + public void setDisplayOverscan(Display display, int left, int top, int right, int bottom) { + // TODO(multi-display): Define policy for secondary displays. + if (display.getDisplayId() == DEFAULT_DISPLAY) { + mOverscanLeft = left; + mOverscanTop = top; + mOverscanRight = right; + mOverscanBottom = bottom; + } + } + public void updateSettings() { ContentResolver resolver = mContext.getContentResolver(); boolean updateRotation = false; @@ -4264,16 +4321,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - // TODO: Should probably be moved into DisplayFrames. public boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds, - DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets, - Rect outOutsets) { + int displayRotation, int displayWidth, int displayHeight, Rect outContentInsets, + Rect outStableInsets, Rect outOutsets) { final int fl = PolicyControl.getWindowFlags(null, attrs); final int sysuiVis = PolicyControl.getSystemUiVisibility(null, attrs); final int systemUiVisibility = (sysuiVis | attrs.subtreeSystemUiVisibility); - final int displayRotation = displayFrames.mRotation; - final int displayWidth = displayFrames.mDisplayWidth; - final int displayHeight = displayFrames.mDisplayHeight; final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl); if (useOutsets) { @@ -4296,33 +4349,34 @@ public class PhoneWindowManager implements WindowManagerPolicy { int availRight, availBottom; if (canHideNavigationBar() && (systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) { - availRight = displayFrames.mUnrestricted.right; - availBottom = displayFrames.mUnrestricted.bottom; + availRight = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth; + availBottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight; } else { - availRight = displayFrames.mRestricted.right; - availBottom = displayFrames.mRestricted.bottom; + availRight = mRestrictedScreenLeft + mRestrictedScreenWidth; + availBottom = mRestrictedScreenTop + mRestrictedScreenHeight; } - outStableInsets.set(displayFrames.mStable.left, displayFrames.mStable.top, - availRight - displayFrames.mStable.right, - availBottom - displayFrames.mStable.bottom); - if ((systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { if ((fl & FLAG_FULLSCREEN) != 0) { - outContentInsets.set(displayFrames.mStableFullscreen.left, - displayFrames.mStableFullscreen.top, - availRight - displayFrames.mStableFullscreen.right, - availBottom - displayFrames.mStableFullscreen.bottom); + outContentInsets.set(mStableFullscreenLeft, mStableFullscreenTop, + availRight - mStableFullscreenRight, + availBottom - mStableFullscreenBottom); } else { - outContentInsets.set(outStableInsets); + outContentInsets.set(mStableLeft, mStableTop, + availRight - mStableRight, availBottom - mStableBottom); } } else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) { outContentInsets.setEmpty(); + } else if ((systemUiVisibility & (View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)) == 0) { + outContentInsets.set(mCurLeft, mCurTop, + availRight - mCurRight, availBottom - mCurBottom); } else { - outContentInsets.set(displayFrames.mCurrent.left, displayFrames.mCurrent.top, - availRight - displayFrames.mCurrent.right, - availBottom - displayFrames.mCurrent.bottom); + outContentInsets.set(mCurLeft, mCurTop, + availRight - mCurRight, availBottom - mCurBottom); } + outStableInsets.set(mStableLeft, mStableTop, + availRight - mStableRight, availBottom - mStableBottom); if (taskBounds != null) { calculateRelevantTaskInsets(taskBounds, outContentInsets, displayWidth, displayHeight); @@ -4359,11 +4413,68 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override - public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) { - displayFrames.onBeginLayout(); - // TODO(multi-display): This doesn't seem right...Maybe only apply to default display? - mSystemGestures.screenWidth = displayFrames.mUnrestricted.width(); - mSystemGestures.screenHeight = displayFrames.mUnrestricted.height(); + public void beginLayoutLw(int displayId, int displayWidth, int displayHeight, + int displayRotation, int uiMode) { + final boolean isDefaultDisplay = displayId == DEFAULT_DISPLAY; + mDisplayRotation = displayRotation; + final int overscanLeft, overscanTop, overscanRight, overscanBottom; + if (isDefaultDisplay) { + switch (displayRotation) { + case Surface.ROTATION_90: + overscanLeft = mOverscanTop; + overscanTop = mOverscanRight; + overscanRight = mOverscanBottom; + overscanBottom = mOverscanLeft; + break; + case Surface.ROTATION_180: + overscanLeft = mOverscanRight; + overscanTop = mOverscanBottom; + overscanRight = mOverscanLeft; + overscanBottom = mOverscanTop; + break; + case Surface.ROTATION_270: + overscanLeft = mOverscanBottom; + overscanTop = mOverscanLeft; + overscanRight = mOverscanTop; + overscanBottom = mOverscanRight; + break; + default: + overscanLeft = mOverscanLeft; + overscanTop = mOverscanTop; + overscanRight = mOverscanRight; + overscanBottom = mOverscanBottom; + break; + } + } else { + overscanLeft = 0; + overscanTop = 0; + overscanRight = 0; + overscanBottom = 0; + } + mOverscanScreenLeft = mRestrictedOverscanScreenLeft = 0; + mOverscanScreenTop = mRestrictedOverscanScreenTop = 0; + mOverscanScreenWidth = mRestrictedOverscanScreenWidth = displayWidth; + mOverscanScreenHeight = mRestrictedOverscanScreenHeight = displayHeight; + mSystemLeft = 0; + mSystemTop = 0; + mSystemRight = displayWidth; + mSystemBottom = displayHeight; + mUnrestrictedScreenLeft = overscanLeft; + mUnrestrictedScreenTop = overscanTop; + mUnrestrictedScreenWidth = displayWidth - overscanLeft - overscanRight; + mUnrestrictedScreenHeight = displayHeight - overscanTop - overscanBottom; + mRestrictedScreenLeft = mUnrestrictedScreenLeft; + mRestrictedScreenTop = mUnrestrictedScreenTop; + mRestrictedScreenWidth = mSystemGestures.screenWidth = mUnrestrictedScreenWidth; + mRestrictedScreenHeight = mSystemGestures.screenHeight = mUnrestrictedScreenHeight; + mDockLeft = mContentLeft = mVoiceContentLeft = mStableLeft = mStableFullscreenLeft + = mCurLeft = mUnrestrictedScreenLeft; + mDockTop = mContentTop = mVoiceContentTop = mStableTop = mStableFullscreenTop + = mCurTop = mUnrestrictedScreenTop; + mDockRight = mContentRight = mVoiceContentRight = mStableRight = mStableFullscreenRight + = mCurRight = displayWidth - overscanRight; + mDockBottom = mContentBottom = mVoiceContentBottom = mStableBottom = mStableFullscreenBottom + = mCurBottom = displayHeight - overscanBottom; mDockLayer = 0x10000000; mStatusBarLayer = -1; @@ -4373,13 +4484,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { final Rect of = mTmpOverscanFrame; final Rect vf = mTmpVisibleFrame; final Rect dcf = mTmpDecorFrame; - vf.set(displayFrames.mDock); - of.set(displayFrames.mDock); - df.set(displayFrames.mDock); - pf.set(displayFrames.mDock); + pf.left = df.left = of.left = vf.left = mDockLeft; + pf.top = df.top = of.top = vf.top = mDockTop; + pf.right = df.right = of.right = vf.right = mDockRight; + pf.bottom = df.bottom = of.bottom = vf.bottom = mDockBottom; dcf.setEmpty(); // Decor frame N/A for system bars. - if (displayFrames.mDisplayId == DEFAULT_DISPLAY) { + if (isDefaultDisplay) { // For purposes of putting out fake window up to steal focus, we will // drive nav being hidden only by whether it is requested. final int sysui = mLastSystemUiFlags; @@ -4398,9 +4509,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { && mStatusBar.getAttrs().height == MATCH_PARENT && mStatusBar.getAttrs().width == MATCH_PARENT; - // When the navigation bar isn't visible, we put up a fake input window to catch all - // touch events. This way we can detect when the user presses anywhere to bring back the - // nav bar and ensure the application doesn't see the event. + // When the navigation bar isn't visible, we put up a fake + // input window to catch all touch events. This way we can + // detect when the user presses anywhere to bring back the nav + // bar and ensure the application doesn't see the event. if (navVisible || navAllowedHidden) { if (mInputConsumer != null) { mHandler.sendMessage( @@ -4416,32 +4528,30 @@ public class PhoneWindowManager implements WindowManagerPolicy { InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL); } - // For purposes of positioning and showing the nav bar, if we have decided that it can't - // be hidden (because of the screen aspect ratio), then take that into account. + // For purposes of positioning and showing the nav bar, if we have + // decided that it can't be hidden (because of the screen aspect ratio), + // then take that into account. navVisible |= !canHideNavigationBar(); - boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, dcf, - navVisible, navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard); - if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock); - updateSysUiVisibility |= layoutStatusBar( - displayFrames, pf, df, of, vf, dcf, sysui, isKeyguardShowing); + boolean updateSysUiVisibility = layoutNavigationBar(displayWidth, displayHeight, + displayRotation, uiMode, overscanLeft, overscanRight, overscanBottom, dcf, navVisible, navTranslucent, + navAllowedHidden, statusBarExpandedNotKeyguard); + if (DEBUG_LAYOUT) Slog.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)", + mDockLeft, mDockTop, mDockRight, mDockBottom)); + updateSysUiVisibility |= layoutStatusBar(pf, df, of, vf, dcf, sysui, isKeyguardShowing); if (updateSysUiVisibility) { updateSystemUiVisibilityLw(); } } - layoutScreenDecorWindows(displayFrames, pf, df, dcf); + layoutScreenDecorWindows(displayId, displayWidth, displayHeight, pf, df, dcf); } - private void layoutScreenDecorWindows(DisplayFrames displayFrames, Rect pf, Rect df, Rect dcf) { + private void layoutScreenDecorWindows(int displayId, int displayWidth, int displayHeight, + Rect pf, Rect df, Rect dcf) { if (mScreenDecorWindows.isEmpty()) { return; } - final int displayId = displayFrames.mDisplayId; - final Rect dockFrame = displayFrames.mDock; - final int displayHeight = displayFrames.mDisplayHeight; - final int displayWidth = displayFrames.mDisplayWidth; - for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) { final WindowState w = mScreenDecorWindows.valueAt(i); if (w.getDisplayId() != displayId || !w.isVisibleLw()) { @@ -4458,10 +4568,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Docked at left or top. if (frame.bottom >= displayHeight) { // Docked left. - dockFrame.left = Math.max(frame.right, dockFrame.left); + mDockLeft = Math.max(frame.right, mDockLeft); } else if (frame.right >= displayWidth ) { // Docked top. - dockFrame.top = Math.max(frame.bottom, dockFrame.top); + mDockTop = Math.max(frame.bottom, mDockTop); } else { Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w + " not docked on left or top of display. frame=" + frame @@ -4471,10 +4581,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Docked at right or bottom. if (frame.top <= 0) { // Docked right. - dockFrame.right = Math.min(frame.left, dockFrame.right); + mDockRight = Math.min(frame.left, mDockRight); } else if (frame.left <= 0) { // Docked bottom. - dockFrame.bottom = Math.min(frame.top, dockFrame.bottom); + mDockBottom = Math.min(frame.top, mDockBottom); } else { Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w + " not docked on right or bottom" + " of display. frame=" + frame @@ -4488,165 +4598,194 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - displayFrames.mRestricted.set(dockFrame); - displayFrames.mCurrent.set(dockFrame); - displayFrames.mVoiceContent.set(dockFrame); - displayFrames.mSystem.set(dockFrame); - displayFrames.mContent.set(dockFrame); - displayFrames.mRestrictedOverscan.set(dockFrame); + mContentTop = mSystemTop = mVoiceContentTop = mCurTop = mRestrictedScreenTop = mDockTop; + mContentLeft = mSystemLeft = mVoiceContentLeft = mCurLeft = mRestrictedScreenLeft + = mRestrictedOverscanScreenLeft = mDockLeft; + mContentBottom = mSystemBottom = mVoiceContentBottom = mCurBottom = mDockBottom; + mContentRight = mSystemRight = mVoiceContentRight = mCurRight = mDockRight; + + mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft; + mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop; + mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft; + mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop; } - private boolean layoutStatusBar(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect vf, - Rect dcf, int sysui, boolean isKeyguardShowing) { + private boolean layoutStatusBar(Rect pf, Rect df, Rect of, Rect vf, Rect dcf, int sysui, + boolean isKeyguardShowing) { // decide where the status bar goes ahead of time - if (mStatusBar == null) { - return false; - } - // apply any navigation bar insets - of.set(displayFrames.mUnrestricted); - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - vf.set(displayFrames.mStable); + if (mStatusBar != null) { + // apply any navigation bar insets + pf.left = df.left = of.left = mUnrestrictedScreenLeft; + pf.top = df.top = of.top = mUnrestrictedScreenTop; + pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft; + pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight + + mUnrestrictedScreenTop; + vf.left = mStableLeft; + vf.top = mStableTop; + vf.right = mStableRight; + vf.bottom = mStableBottom; - mStatusBarLayer = mStatusBar.getSurfaceLayer(); + mStatusBarLayer = mStatusBar.getSurfaceLayer(); - // Let the status bar determine its size. - mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */, - vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */, - dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */); + // Let the status bar determine its size. + mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */, + vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */, + dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */); - // For layout, the status bar is always at the top with our fixed height. - displayFrames.mStable.top = displayFrames.mUnrestricted.top + mStatusBarHeight; + // For layout, the status bar is always at the top with our fixed height. + mStableTop = mUnrestrictedScreenTop + mStatusBarHeight; - boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0; - boolean statusBarTranslucent = (sysui - & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0; - if (!isKeyguardShowing) { - statusBarTranslucent &= areTranslucentBarsAllowed(); - } + boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0; + boolean statusBarTranslucent = (sysui + & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0; + if (!isKeyguardShowing) { + statusBarTranslucent &= areTranslucentBarsAllowed(); + } - // If the status bar is hidden, we don't want to cause windows behind it to scroll. - if (mStatusBar.isVisibleLw() && !statusBarTransient) { - // Status bar may go away, so the screen area it occupies is available to apps but just - // covering them when the status bar is visible. - final Rect dockFrame = displayFrames.mDock; - dockFrame.top = displayFrames.mStable.top; - displayFrames.mContent.set(dockFrame); - displayFrames.mVoiceContent.set(dockFrame); + // If the status bar is hidden, we don't want to cause + // windows behind it to scroll. + if (mStatusBar.isVisibleLw() && !statusBarTransient) { + // Status bar may go away, so the screen area it occupies + // is available to apps but just covering them when the + // status bar is visible. + mDockTop = mUnrestrictedScreenTop + mStatusBarHeight; - if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format( - "dock=%s content=%s cur=%s", dockFrame.toString(), - displayFrames.mContent.toString(), displayFrames.mCurrent.toString())); + mContentTop = mVoiceContentTop = mCurTop = mDockTop; + mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom; + mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft; + mContentRight = mVoiceContentRight = mCurRight = mDockRight; - if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent + if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + + String.format( + "dock=[%d,%d][%d,%d] content=[%d,%d][%d,%d] cur=[%d,%d][%d,%d]", + mDockLeft, mDockTop, mDockRight, mDockBottom, + mContentLeft, mContentTop, mContentRight, mContentBottom, + mCurLeft, mCurTop, mCurRight, mCurBottom)); + } + if (mStatusBar.isVisibleLw() && !mStatusBar.isAnimatingLw() + && !statusBarTransient && !statusBarTranslucent && !mStatusBarController.wasRecentlyTranslucent()) { - // If the opaque status bar is currently requested to be visible, and not in the - // process of animating on or off, then we can tell the app that it is covered by it. - displayFrames.mSystem.top = displayFrames.mStable.top; + // If the opaque status bar is currently requested to be visible, + // and not in the process of animating on or off, then + // we can tell the app that it is covered by it. + mSystemTop = mUnrestrictedScreenTop + mStatusBarHeight; + } + if (mStatusBarController.checkHiddenLw()) { + return true; } } - return mStatusBarController.checkHiddenLw(); + return false; } - private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, Rect dcf, + private boolean layoutNavigationBar(int displayWidth, int displayHeight, int displayRotation, + int uiMode, int overscanLeft, int overscanRight, int overscanBottom, Rect dcf, boolean navVisible, boolean navTranslucent, boolean navAllowedHidden, boolean statusBarExpandedNotKeyguard) { - if (mNavigationBar == null) { - return false; - } - boolean transientNavBarShowing = mNavigationBarController.isTransientShowing(); - // Force the navigation bar to its appropriate place and size. We need to do this directly, - // instead of relying on it to bubble up from the nav bar, because this needs to change - // atomically with screen rotations. - final int rotation = displayFrames.mRotation; - final int displayHeight = displayFrames.mDisplayHeight; - final int displayWidth = displayFrames.mDisplayWidth; - final Rect dockFrame = displayFrames.mDock; - mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation); - - if (mNavigationBarPosition == NAV_BAR_BOTTOM) { - // It's a system nav bar or a portrait screen; nav bar goes on bottom. - final int top = displayFrames.mUnrestricted.bottom - - getNavigationBarHeight(rotation, uiMode); - mTmpNavigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom); - displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top; - if (transientNavBarShowing) { - mNavigationBarController.setBarShowingLw(true); - } else if (navVisible) { - mNavigationBarController.setBarShowingLw(true); - dockFrame.bottom = displayFrames.mRestricted.bottom - = displayFrames.mRestrictedOverscan.bottom = top; - } else { - // We currently want to hide the navigation UI - unless we expanded the status bar. - mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard); + if (mNavigationBar != null) { + boolean transientNavBarShowing = mNavigationBarController.isTransientShowing(); + // Force the navigation bar to its appropriate place and + // size. We need to do this directly, instead of relying on + // it to bubble up from the nav bar, because this needs to + // change atomically with screen rotations. + mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, + displayRotation); + if (mNavigationBarPosition == NAV_BAR_BOTTOM) { + // It's a system nav bar or a portrait screen; nav bar goes on bottom. + int top = displayHeight - overscanBottom + - getNavigationBarHeight(displayRotation, uiMode); + mTmpNavigationFrame.set(0, top, displayWidth, displayHeight - overscanBottom); + mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top; + if (transientNavBarShowing) { + mNavigationBarController.setBarShowingLw(true); + } else if (navVisible) { + mNavigationBarController.setBarShowingLw(true); + mDockBottom = mTmpNavigationFrame.top; + mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop; + mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop; + } else { + // We currently want to hide the navigation UI - unless we expanded the status + // bar. + mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard); + } + if (navVisible && !navTranslucent && !navAllowedHidden + && !mNavigationBar.isAnimatingLw() + && !mNavigationBarController.wasRecentlyTranslucent()) { + // If the opaque nav bar is currently requested to be visible, + // and not in the process of animating on or off, then + // we can tell the app that it is covered by it. + mSystemBottom = mTmpNavigationFrame.top; + } + } else if (mNavigationBarPosition == NAV_BAR_RIGHT) { + // Landscape screen; nav bar goes to the right. + int left = displayWidth - overscanRight + - getNavigationBarWidth(displayRotation, uiMode); + mTmpNavigationFrame.set(left, 0, displayWidth - overscanRight, displayHeight); + mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left; + if (transientNavBarShowing) { + mNavigationBarController.setBarShowingLw(true); + } else if (navVisible) { + mNavigationBarController.setBarShowingLw(true); + mDockRight = mTmpNavigationFrame.left; + mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft; + mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft; + } else { + // We currently want to hide the navigation UI - unless we expanded the status + // bar. + mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard); + } + if (navVisible && !navTranslucent && !navAllowedHidden + && !mNavigationBar.isAnimatingLw() + && !mNavigationBarController.wasRecentlyTranslucent()) { + // If the nav bar is currently requested to be visible, + // and not in the process of animating on or off, then + // we can tell the app that it is covered by it. + mSystemRight = mTmpNavigationFrame.left; + } + } else if (mNavigationBarPosition == NAV_BAR_LEFT) { + // Seascape screen; nav bar goes to the left. + int right = overscanLeft + getNavigationBarWidth(displayRotation, uiMode); + mTmpNavigationFrame.set(overscanLeft, 0, right, displayHeight); + mStableLeft = mStableFullscreenLeft = mTmpNavigationFrame.right; + if (transientNavBarShowing) { + mNavigationBarController.setBarShowingLw(true); + } else if (navVisible) { + mNavigationBarController.setBarShowingLw(true); + mDockLeft = mTmpNavigationFrame.right; + // TODO: not so sure about those: + mRestrictedScreenLeft = mRestrictedOverscanScreenLeft = mDockLeft; + mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft; + mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft; + } else { + // We currently want to hide the navigation UI - unless we expanded the status + // bar. + mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard); + } + if (navVisible && !navTranslucent && !navAllowedHidden + && !mNavigationBar.isAnimatingLw() + && !mNavigationBarController.wasRecentlyTranslucent()) { + // If the nav bar is currently requested to be visible, + // and not in the process of animating on or off, then + // we can tell the app that it is covered by it. + mSystemLeft = mTmpNavigationFrame.right; + } } - if (navVisible && !navTranslucent && !navAllowedHidden - && !mNavigationBar.isAnimatingLw() - && !mNavigationBarController.wasRecentlyTranslucent()) { - // If the opaque nav bar is currently requested to be visible and not in the process - // of animating on or off, then we can tell the app that it is covered by it. - displayFrames.mSystem.bottom = top; - } - } else if (mNavigationBarPosition == NAV_BAR_RIGHT) { - // Landscape screen; nav bar goes to the right. - final int left = displayFrames.mUnrestricted.right - - getNavigationBarWidth(rotation, uiMode); - mTmpNavigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight); - displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left; - if (transientNavBarShowing) { - mNavigationBarController.setBarShowingLw(true); - } else if (navVisible) { - mNavigationBarController.setBarShowingLw(true); - dockFrame.right = displayFrames.mRestricted.right - = displayFrames.mRestrictedOverscan.right = left; - } else { - // We currently want to hide the navigation UI - unless we expanded the status bar. - mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard); - } - if (navVisible && !navTranslucent && !navAllowedHidden - && !mNavigationBar.isAnimatingLw() - && !mNavigationBarController.wasRecentlyTranslucent()) { - // If the nav bar is currently requested to be visible, and not in the process of - // animating on or off, then we can tell the app that it is covered by it. - displayFrames.mSystem.right = left; - } - } else if (mNavigationBarPosition == NAV_BAR_LEFT) { - // Seascape screen; nav bar goes to the left. - final int right = displayFrames.mUnrestricted.left - - getNavigationBarWidth(rotation, uiMode); - mTmpNavigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight); - displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right; - if (transientNavBarShowing) { - mNavigationBarController.setBarShowingLw(true); - } else if (navVisible) { - mNavigationBarController.setBarShowingLw(true); - dockFrame.left = displayFrames.mRestricted.left = - displayFrames.mRestrictedOverscan.left = right; - } else { - // We currently want to hide the navigation UI - unless we expanded the status bar. - mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard); - } - if (navVisible && !navTranslucent && !navAllowedHidden - && !mNavigationBar.isAnimatingLw() - && !mNavigationBarController.wasRecentlyTranslucent()) { - // If the nav bar is currently requested to be visible, and not in the process of - // animating on or off, then we can tell the app that it is covered by it. - displayFrames.mSystem.left = right; + // Make sure the content and current rectangles are updated to + // account for the restrictions from the navigation bar. + mContentTop = mVoiceContentTop = mCurTop = mDockTop; + mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom; + mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft; + mContentRight = mVoiceContentRight = mCurRight = mDockRight; + mStatusBarLayer = mNavigationBar.getSurfaceLayer(); + // And compute the final frame. + mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame, + mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf, + mTmpNavigationFrame, mTmpNavigationFrame); + if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame); + if (mNavigationBarController.checkHiddenLw()) { + return true; } } - - // Make sure the content and current rectangles are updated to account for the restrictions - // from the navigation bar. - displayFrames.mCurrent.set(dockFrame); - displayFrames.mVoiceContent.set(dockFrame); - displayFrames.mContent.set(dockFrame); - mStatusBarLayer = mNavigationBar.getSurfaceLayer(); - // And compute the final frame. - mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame, - mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf, - mTmpNavigationFrame, mTmpNavigationFrame); - if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame); - return mNavigationBarController.checkHiddenLw(); + return false; } private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) { @@ -4674,26 +4813,32 @@ public class PhoneWindowManager implements WindowManagerPolicy { return 0; } - private void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached, - boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf, - DisplayFrames displayFrames) { + @Override + public void getContentRectLw(Rect r) { + r.set(mContentLeft, mContentTop, mContentRight, mContentBottom); + } + + void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached, + boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf) { if (win.getSurfaceLayer() > mDockLayer && attached.getSurfaceLayer() < mDockLayer) { - // Here's a special case: if this attached window is a panel that is above the dock - // window, and the window it is attached to is below the dock window, then the frames we - // computed for the window it is attached to can not be used because the dock is - // effectively part of the underlying window and the attached window is floating on top - // of the whole thing. So, we ignore the attached window and explicitly compute the - // frames that would be appropriate without the dock. - vf.set(displayFrames.mDock); - cf.set(displayFrames.mDock); - of.set(displayFrames.mDock); - df.set(displayFrames.mDock); + // Here's a special case: if this attached window is a panel that is + // above the dock window, and the window it is attached to is below + // the dock window, then the frames we computed for the window it is + // attached to can not be used because the dock is effectively part + // of the underlying window and the attached window is floating on top + // of the whole thing. So, we ignore the attached window and explicitly + // compute the frames that would be appropriate without the dock. + df.left = of.left = cf.left = vf.left = mDockLeft; + df.top = of.top = cf.top = vf.top = mDockTop; + df.right = of.right = cf.right = vf.right = mDockRight; + df.bottom = of.bottom = cf.bottom = vf.bottom = mDockBottom; } else { - // The effective display frame of the attached window depends on whether it is taking - // care of insetting its content. If not, we need to use the parent's content frame so - // that the entire window is positioned within that content. Otherwise we can use the - // overscan frame and let the attached window take care of positioning its content - // appropriately. + // The effective display frame of the attached window depends on + // whether it is taking care of insetting its content. If not, + // we need to use the parent's content frame so that the entire + // window is positioned within that content. Otherwise we can use + // the overscan frame and let the attached window take care of + // positioning its content appropriately. if (adjust != SOFT_INPUT_ADJUST_RESIZE) { // Set the content frame of the attached window to the parent's decor frame // (same as content frame when IME isn't present) if specifically requested by @@ -4702,37 +4847,51 @@ public class PhoneWindowManager implements WindowManagerPolicy { cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0 ? attached.getContentFrameLw() : attached.getOverscanFrameLw()); } else { - // If the window is resizing, then we want to base the content frame on our attached - // content frame to resize...however, things can be tricky if the attached window is - // NOT in resize mode, in which case its content frame will be larger. - // Ungh. So to deal with that, make sure the content frame we end up using is not - // covering the IM dock. + // If the window is resizing, then we want to base the content + // frame on our attached content frame to resize... however, + // things can be tricky if the attached window is NOT in resize + // mode, in which case its content frame will be larger. + // Ungh. So to deal with that, make sure the content frame + // we end up using is not covering the IM dock. cf.set(attached.getContentFrameLw()); if (attached.isVoiceInteraction()) { - cf.intersectUnchecked(displayFrames.mVoiceContent); + if (cf.left < mVoiceContentLeft) cf.left = mVoiceContentLeft; + if (cf.top < mVoiceContentTop) cf.top = mVoiceContentTop; + if (cf.right > mVoiceContentRight) cf.right = mVoiceContentRight; + if (cf.bottom > mVoiceContentBottom) cf.bottom = mVoiceContentBottom; } else if (attached.getSurfaceLayer() < mDockLayer) { - cf.intersectUnchecked(displayFrames.mContent); + if (cf.left < mContentLeft) cf.left = mContentLeft; + if (cf.top < mContentTop) cf.top = mContentTop; + if (cf.right > mContentRight) cf.right = mContentRight; + if (cf.bottom > mContentBottom) cf.bottom = mContentBottom; } } df.set(insetDecors ? attached.getDisplayFrameLw() : cf); of.set(insetDecors ? attached.getOverscanFrameLw() : cf); vf.set(attached.getVisibleFrameLw()); } - // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be - // positioned relative to its parent or the entire screen. - pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df); + // The LAYOUT_IN_SCREEN flag is used to determine whether the attached + // window should be positioned relative to its parent or the entire + // screen. + pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 + ? attached.getFrameLw() : df); } - private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) { - if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) == 0) { - return; - } - // If app is requesting a stable layout, don't let the content insets go below the stable - // values. - if ((fl & FLAG_FULLSCREEN) != 0) { - r.intersectUnchecked(displayFrames.mStableFullscreen); - } else { - r.intersectUnchecked(displayFrames.mStable); + private void applyStableConstraints(int sysui, int fl, Rect r) { + if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { + // If app is requesting a stable layout, don't let the + // content insets go below the stable values. + if ((fl & FLAG_FULLSCREEN) != 0) { + if (r.left < mStableFullscreenLeft) r.left = mStableFullscreenLeft; + if (r.top < mStableFullscreenTop) r.top = mStableFullscreenTop; + if (r.right > mStableFullscreenRight) r.right = mStableFullscreenRight; + if (r.bottom > mStableFullscreenBottom) r.bottom = mStableFullscreenBottom; + } else { + if (r.left < mStableLeft) r.left = mStableLeft; + if (r.top < mStableTop) r.top = mStableTop; + if (r.right > mStableRight) r.right = mStableRight; + if (r.bottom > mStableBottom) r.bottom = mStableBottom; + } } } @@ -4747,7 +4906,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override - public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) { + public void layoutWindowLw(WindowState win, WindowState attached) { // We've already done the navigation bar, status bar, and all screen decor windows. If the // status bar can receive input, we need to layout it again to accommodate for the IME // window. @@ -4761,10 +4920,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { (win == mLastInputMethodTargetWindow && mLastInputMethodWindow != null); if (needsToOffsetInputMethodTarget) { if (DEBUG_LAYOUT) Slog.i(TAG, "Offset ime target window by the last ime window state"); - offsetInputMethodWindowLw(mLastInputMethodWindow, displayFrames); + offsetInputMethodWindowLw(mLastInputMethodWindow); } - final int type = attrs.type; final int fl = PolicyControl.getWindowFlags(win, attrs); final int pfl = attrs.privateFlags; final int sim = attrs.softInputMode; @@ -4785,83 +4943,120 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int adjust = sim & SOFT_INPUT_MASK_ADJUST; - sf.set(displayFrames.mStable); + if (isDefaultDisplay) { + sf.set(mStableLeft, mStableTop, mStableRight, mStableBottom); + } else { + sf.set(mOverscanLeft, mOverscanTop, mOverscanRight, mOverscanBottom); + } - if (type == TYPE_INPUT_METHOD) { - vf.set(displayFrames.mDock); - cf.set(displayFrames.mDock); - of.set(displayFrames.mDock); - df.set(displayFrames.mDock); - pf.set(displayFrames.mDock); + if (!isDefaultDisplay) { + // TODO: Need to fix this and above to take into account decor windows. + if (attached != null) { + // If this window is attached to another, our display + // frame is the same as the one we are attached to. + setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf); + } else { + // Give the window full screen. + pf.left = df.left = of.left = cf.left = mOverscanScreenLeft; + pf.top = df.top = of.top = cf.top = mOverscanScreenTop; + pf.right = df.right = of.right = cf.right + = mOverscanScreenLeft + mOverscanScreenWidth; + pf.bottom = df.bottom = of.bottom = cf.bottom + = mOverscanScreenTop + mOverscanScreenHeight; + } + } else if (attrs.type == TYPE_INPUT_METHOD) { + pf.left = df.left = of.left = cf.left = vf.left = mDockLeft; + pf.top = df.top = of.top = cf.top = vf.top = mDockTop; + pf.right = df.right = of.right = cf.right = vf.right = mDockRight; // IM dock windows layout below the nav bar... - pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom; + pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight; // ...with content insets above the nav bar - cf.bottom = vf.bottom = displayFrames.mStable.bottom; + cf.bottom = vf.bottom = mStableBottom; if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) { // The status bar forces the navigation bar while it's visible. Make sure the IME // avoids the navigation bar in that case. if (mNavigationBarPosition == NAV_BAR_RIGHT) { - pf.right = df.right = of.right = cf.right = vf.right = - displayFrames.mStable.right; + pf.right = df.right = of.right = cf.right = vf.right = mStableRight; } else if (mNavigationBarPosition == NAV_BAR_LEFT) { - pf.left = df.left = of.left = cf.left = vf.left = displayFrames.mStable.left; + pf.left = df.left = of.left = cf.left = vf.left = mStableLeft; } } // IM dock windows always go to the bottom of the screen. attrs.gravity = Gravity.BOTTOM; mDockLayer = win.getSurfaceLayer(); - } else if (type == TYPE_VOICE_INTERACTION) { - of.set(displayFrames.mUnrestricted); - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); + } else if (attrs.type == TYPE_VOICE_INTERACTION) { + pf.left = df.left = of.left = mUnrestrictedScreenLeft; + pf.top = df.top = of.top = mUnrestrictedScreenTop; + pf.right = df.right = of.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth; + pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight; if (adjust != SOFT_INPUT_ADJUST_RESIZE) { - cf.set(displayFrames.mDock); + cf.left = mDockLeft; + cf.top = mDockTop; + cf.right = mDockRight; + cf.bottom = mDockBottom; } else { - cf.set(displayFrames.mContent); + cf.left = mContentLeft; + cf.top = mContentTop; + cf.right = mContentRight; + cf.bottom = mContentBottom; } if (adjust != SOFT_INPUT_ADJUST_NOTHING) { - vf.set(displayFrames.mCurrent); + vf.left = mCurLeft; + vf.top = mCurTop; + vf.right = mCurRight; + vf.bottom = mCurBottom; } else { vf.set(cf); } - } else if (type == TYPE_WALLPAPER) { - layoutWallpaper(displayFrames, pf, df, of, cf); + } else if (attrs.type == TYPE_WALLPAPER) { + layoutWallpaper(win, pf, df, of, cf); } else if (win == mStatusBar) { - of.set(displayFrames.mUnrestricted); - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - cf.set(displayFrames.mStable); - vf.set(displayFrames.mStable); + pf.left = df.left = of.left = mUnrestrictedScreenLeft; + pf.top = df.top = of.top = mUnrestrictedScreenTop; + pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft; + pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight + mUnrestrictedScreenTop; + cf.left = vf.left = mStableLeft; + cf.top = vf.top = mStableTop; + cf.right = vf.right = mStableRight; + vf.bottom = mStableBottom; if (adjust == SOFT_INPUT_ADJUST_RESIZE) { - cf.bottom = displayFrames.mContent.bottom; + cf.bottom = mContentBottom; } else { - cf.bottom = displayFrames.mDock.bottom; - vf.bottom = displayFrames.mContent.bottom; + cf.bottom = mDockBottom; + vf.bottom = mContentBottom; } } else { - dcf.set(displayFrames.mSystem); - final boolean inheritTranslucentDecor = - (attrs.privateFlags & PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0; + + // Default policy decor for the default display + dcf.left = mSystemLeft; + dcf.top = mSystemTop; + dcf.right = mSystemRight; + dcf.bottom = mSystemBottom; + final boolean inheritTranslucentDecor = (attrs.privateFlags + & WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0; final boolean isAppWindow = - type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW; + attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW && + attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; final boolean topAtRest = win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw(); if (isAppWindow && !inheritTranslucentDecor && !topAtRest) { if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 - && (fl & FLAG_FULLSCREEN) == 0 - && (fl & FLAG_TRANSLUCENT_STATUS) == 0 - && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 + && (fl & WindowManager.LayoutParams.FLAG_FULLSCREEN) == 0 + && (fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) == 0 + && (fl & WindowManager.LayoutParams. + FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 && (pfl & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) == 0) { // Ensure policy decor includes status bar - dcf.top = displayFrames.mStable.top; + dcf.top = mStableTop; } - if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0 + if ((fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) == 0 && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0 - && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) { + && (fl & WindowManager.LayoutParams. + FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) { // Ensure policy decor includes navigation bar - dcf.bottom = displayFrames.mStable.bottom; - dcf.right = displayFrames.mStable.right; + dcf.bottom = mStableBottom; + dcf.right = mStableRight; } } @@ -4869,83 +5064,117 @@ public class PhoneWindowManager implements WindowManagerPolicy { == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) { if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): IN_SCREEN, INSET_DECOR"); - // This is the case for a normal activity window: we want it to cover all of the - // screen space, and it can take care of moving its contents to account for screen - // decorations that intrude into that space. + // This is the case for a normal activity window: we want it + // to cover all of the screen space, and it can take care of + // moving its contents to account for screen decorations that + // intrude into that space. if (attached != null) { // If this window is attached to another, our display // frame is the same as the one we are attached to. - setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf, - displayFrames); + setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf); } else { - if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) { - // Status bar panels are the only windows who can go on top of the status - // bar. They are protected by the STATUS_BAR_SERVICE permission, so they - // have the same privileges as the status bar itself. + if (attrs.type == TYPE_STATUS_BAR_PANEL + || attrs.type == TYPE_STATUS_BAR_SUB_PANEL) { + // Status bar panels are the only windows who can go on top of + // the status bar. They are protected by the STATUS_BAR_SERVICE + // permission, so they have the same privileges as the status + // bar itself. // // However, they should still dodge the navigation bar if it exists. pf.left = df.left = of.left = hasNavBar - ? displayFrames.mDock.left : displayFrames.mUnrestricted.left; - pf.top = df.top = of.top = displayFrames.mUnrestricted.top; + ? mDockLeft : mUnrestrictedScreenLeft; + pf.top = df.top = of.top = mUnrestrictedScreenTop; pf.right = df.right = of.right = hasNavBar - ? displayFrames.mRestricted.right - : displayFrames.mUnrestricted.right; + ? mRestrictedScreenLeft+mRestrictedScreenWidth + : mUnrestrictedScreenLeft + mUnrestrictedScreenWidth; pf.bottom = df.bottom = of.bottom = hasNavBar - ? displayFrames.mRestricted.bottom - : displayFrames.mUnrestricted.bottom; + ? mRestrictedScreenTop+mRestrictedScreenHeight + : mUnrestrictedScreenTop + mUnrestrictedScreenHeight; if (DEBUG_LAYOUT) Slog.v(TAG, String.format( "Laying out status bar window: (%d,%d - %d,%d)", pf.left, pf.top, pf.right, pf.bottom)); } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0 - && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) { + && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW + && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { // Asking to layout into the overscan region, so give it that pure // unrestricted area. - of.set(displayFrames.mOverscan); - df.set(displayFrames.mOverscan); - pf.set(displayFrames.mOverscan); + pf.left = df.left = of.left = mOverscanScreenLeft; + pf.top = df.top = of.top = mOverscanScreenTop; + pf.right = df.right = of.right = mOverscanScreenLeft + mOverscanScreenWidth; + pf.bottom = df.bottom = of.bottom = mOverscanScreenTop + + mOverscanScreenHeight; } else if (canHideNavigationBar() && (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 - && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) { - // Asking for layout as if the nav bar is hidden, lets the application - // extend into the unrestricted overscan screen area. We only do this for - // application windows to ensure no window that can be above the nav bar can - // do this. - df.set(displayFrames.mOverscan); - pf.set(displayFrames.mOverscan); - // We need to tell the app about where the frame inside the overscan is, so - // it can inset its content by that amount -- it didn't ask to actually - // extend itself into the overscan region. - of.set(displayFrames.mUnrestricted); - } else { - df.set(displayFrames.mRestrictedOverscan); - pf.set(displayFrames.mRestrictedOverscan); + && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW + && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { + // Asking for layout as if the nav bar is hidden, lets the + // application extend into the unrestricted overscan screen area. We + // only do this for application windows to ensure no window that + // can be above the nav bar can do this. + pf.left = df.left = mOverscanScreenLeft; + pf.top = df.top = mOverscanScreenTop; + pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth; + pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight; // We need to tell the app about where the frame inside the overscan // is, so it can inset its content by that amount -- it didn't ask // to actually extend itself into the overscan region. - of.set(displayFrames.mUnrestricted); + of.left = mUnrestrictedScreenLeft; + of.top = mUnrestrictedScreenTop; + of.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth; + of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight; + } else { + pf.left = df.left = mRestrictedOverscanScreenLeft; + pf.top = df.top = mRestrictedOverscanScreenTop; + pf.right = df.right = mRestrictedOverscanScreenLeft + + mRestrictedOverscanScreenWidth; + pf.bottom = df.bottom = mRestrictedOverscanScreenTop + + mRestrictedOverscanScreenHeight; + // We need to tell the app about where the frame inside the overscan + // is, so it can inset its content by that amount -- it didn't ask + // to actually extend itself into the overscan region. + of.left = mUnrestrictedScreenLeft; + of.top = mUnrestrictedScreenTop; + of.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth; + of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight; } if ((fl & FLAG_FULLSCREEN) == 0) { if (win.isVoiceInteraction()) { - cf.set(displayFrames.mVoiceContent); + cf.left = mVoiceContentLeft; + cf.top = mVoiceContentTop; + cf.right = mVoiceContentRight; + cf.bottom = mVoiceContentBottom; } else { if (adjust != SOFT_INPUT_ADJUST_RESIZE) { - cf.set(displayFrames.mDock); + cf.left = mDockLeft; + cf.top = mDockTop; + cf.right = mDockRight; + cf.bottom = mDockBottom; } else { - cf.set(displayFrames.mContent); + cf.left = mContentLeft; + cf.top = mContentTop; + cf.right = mContentRight; + cf.bottom = mContentBottom; } } } else { - // Full screen windows are always given a layout that is as if the status - // bar and other transient decors are gone. This is to avoid bad states when - // moving from a window that is not hiding the status bar to one that is. - cf.set(displayFrames.mRestricted); + // Full screen windows are always given a layout that is as if the + // status bar and other transient decors are gone. This is to avoid + // bad states when moving from a window that is not hding the + // status bar to one that is. + cf.left = mRestrictedScreenLeft; + cf.top = mRestrictedScreenTop; + cf.right = mRestrictedScreenLeft + mRestrictedScreenWidth; + cf.bottom = mRestrictedScreenTop + mRestrictedScreenHeight; } - applyStableConstraints(sysUiFl, fl, cf, displayFrames); + applyStableConstraints(sysUiFl, fl, cf); if (adjust != SOFT_INPUT_ADJUST_NOTHING) { - vf.set(displayFrames.mCurrent); + vf.left = mCurLeft; + vf.top = mCurTop; + vf.right = mCurRight; + vf.bottom = mCurBottom; } else { vf.set(cf); } @@ -4953,70 +5182,86 @@ public class PhoneWindowManager implements WindowManagerPolicy { } else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0 || (sysUiFl & (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) { - if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() - + "): IN_SCREEN"); + if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + + "): IN_SCREEN"); // A window that has requested to fill the entire screen just // gets everything, period. - if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) { - cf.set(displayFrames.mUnrestricted); - of.set(displayFrames.mUnrestricted); - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - if (hasNavBar) { - pf.left = df.left = of.left = cf.left = displayFrames.mDock.left; - pf.right = df.right = of.right = cf.right = displayFrames.mRestricted.right; - pf.bottom = df.bottom = of.bottom = cf.bottom = - displayFrames.mRestricted.bottom; - } + if (attrs.type == TYPE_STATUS_BAR_PANEL + || attrs.type == TYPE_STATUS_BAR_SUB_PANEL) { + pf.left = df.left = of.left = cf.left = hasNavBar + ? mDockLeft : mUnrestrictedScreenLeft; + pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop; + pf.right = df.right = of.right = cf.right = hasNavBar + ? mRestrictedScreenLeft + mRestrictedScreenWidth + : mUnrestrictedScreenLeft + mUnrestrictedScreenWidth; + pf.bottom = df.bottom = of.bottom = cf.bottom = hasNavBar + ? mRestrictedScreenTop + mRestrictedScreenHeight + : mUnrestrictedScreenTop + mUnrestrictedScreenHeight; if (DEBUG_LAYOUT) Slog.v(TAG, String.format( "Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)", pf.left, pf.top, pf.right, pf.bottom)); - } else if (type == TYPE_VOLUME_OVERLAY) { + } else if (attrs.type == TYPE_VOLUME_OVERLAY) { // Volume overlay covers everything, including the status and navbar - cf.set(displayFrames.mUnrestricted); - of.set(displayFrames.mUnrestricted); - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); + pf.left = df.left = of.left = cf.left = mUnrestrictedScreenLeft; + pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop; + pf.right = df.right = of.right = cf.right = + mUnrestrictedScreenLeft + mUnrestrictedScreenWidth; + pf.bottom = df.bottom = of.bottom = cf.bottom = + mUnrestrictedScreenTop + mUnrestrictedScreenHeight; if (DEBUG_LAYOUT) Slog.v(TAG, String.format( "Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)", pf.left, pf.top, pf.right, pf.bottom)); - } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) { + } else if (attrs.type == TYPE_NAVIGATION_BAR + || attrs.type == TYPE_NAVIGATION_BAR_PANEL) { // The navigation bar has Real Ultimate Power. - of.set(displayFrames.mUnrestricted); - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); + pf.left = df.left = of.left = mUnrestrictedScreenLeft; + pf.top = df.top = of.top = mUnrestrictedScreenTop; + pf.right = df.right = of.right = mUnrestrictedScreenLeft + + mUnrestrictedScreenWidth; + pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop + + mUnrestrictedScreenHeight; if (DEBUG_LAYOUT) Slog.v(TAG, String.format( "Laying out navigation bar window: (%d,%d - %d,%d)", pf.left, pf.top, pf.right, pf.bottom)); - } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT) + } else if ((attrs.type == TYPE_SECURE_SYSTEM_OVERLAY + || attrs.type == TYPE_BOOT_PROGRESS + || attrs.type == TYPE_SCREENSHOT) && ((fl & FLAG_FULLSCREEN) != 0)) { // Fullscreen secure system overlays get what they ask for. Screenshot region // selection overlay should also expand to full screen. - cf.set(displayFrames.mOverscan); - of.set(displayFrames.mOverscan); - df.set(displayFrames.mOverscan); - pf.set(displayFrames.mOverscan); - } else if (type == TYPE_BOOT_PROGRESS) { + pf.left = df.left = of.left = cf.left = mOverscanScreenLeft; + pf.top = df.top = of.top = cf.top = mOverscanScreenTop; + pf.right = df.right = of.right = cf.right = mOverscanScreenLeft + + mOverscanScreenWidth; + pf.bottom = df.bottom = of.bottom = cf.bottom = mOverscanScreenTop + + mOverscanScreenHeight; + } else if (attrs.type == TYPE_BOOT_PROGRESS) { // Boot progress screen always covers entire display. - cf.set(displayFrames.mOverscan); - of.set(displayFrames.mOverscan); - df.set(displayFrames.mOverscan); - pf.set(displayFrames.mOverscan); + pf.left = df.left = of.left = cf.left = mOverscanScreenLeft; + pf.top = df.top = of.top = cf.top = mOverscanScreenTop; + pf.right = df.right = of.right = cf.right = mOverscanScreenLeft + + mOverscanScreenWidth; + pf.bottom = df.bottom = of.bottom = cf.bottom = mOverscanScreenTop + + mOverscanScreenHeight; } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0 - && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) { - // Asking to layout into the overscan region, so give it that pure unrestricted - // area. - cf.set(displayFrames.mOverscan); - of.set(displayFrames.mOverscan); - df.set(displayFrames.mOverscan); - pf.set(displayFrames.mOverscan); + && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW + && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { + // Asking to layout into the overscan region, so give it that pure + // unrestricted area. + pf.left = df.left = of.left = cf.left = mOverscanScreenLeft; + pf.top = df.top = of.top = cf.top = mOverscanScreenTop; + pf.right = df.right = of.right = cf.right + = mOverscanScreenLeft + mOverscanScreenWidth; + pf.bottom = df.bottom = of.bottom = cf.bottom + = mOverscanScreenTop + mOverscanScreenHeight; } else if (canHideNavigationBar() && (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 - && (type == TYPE_STATUS_BAR - || type == TYPE_TOAST - || type == TYPE_DOCK_DIVIDER - || type == TYPE_VOICE_INTERACTION_STARTING - || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) { + && (attrs.type == TYPE_STATUS_BAR + || attrs.type == TYPE_TOAST + || attrs.type == TYPE_DOCK_DIVIDER + || attrs.type == TYPE_VOICE_INTERACTION_STARTING + || (attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW + && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW))) { // Asking for layout as if the nav bar is hidden, lets the // application extend into the unrestricted screen area. We // only do this for application windows (or toasts) to ensure no window that @@ -5024,76 +5269,102 @@ public class PhoneWindowManager implements WindowManagerPolicy { // XXX This assumes that an app asking for this will also // ask for layout in only content. We can't currently figure out // what the screen would be if only laying out to hide the nav bar. - cf.set(displayFrames.mUnrestricted); - of.set(displayFrames.mUnrestricted); - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); + pf.left = df.left = of.left = cf.left = mUnrestrictedScreenLeft; + pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop; + pf.right = df.right = of.right = cf.right = mUnrestrictedScreenLeft + + mUnrestrictedScreenWidth; + pf.bottom = df.bottom = of.bottom = cf.bottom = mUnrestrictedScreenTop + + mUnrestrictedScreenHeight; } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) { - of.set(displayFrames.mRestricted); - df.set(displayFrames.mRestricted); - pf.set(displayFrames.mRestricted); + pf.left = df.left = of.left = mRestrictedScreenLeft; + pf.top = df.top = of.top = mRestrictedScreenTop; + pf.right = df.right = of.right = mRestrictedScreenLeft + mRestrictedScreenWidth; + pf.bottom = df.bottom = of.bottom = mRestrictedScreenTop + + mRestrictedScreenHeight; if (adjust != SOFT_INPUT_ADJUST_RESIZE) { - cf.set(displayFrames.mDock); + cf.left = mDockLeft; + cf.top = mDockTop; + cf.right = mDockRight; + cf.bottom = mDockBottom; } else { - cf.set(displayFrames.mContent); + cf.left = mContentLeft; + cf.top = mContentTop; + cf.right = mContentRight; + cf.bottom = mContentBottom; } } else { - cf.set(displayFrames.mRestricted); - of.set(displayFrames.mRestricted); - df.set(displayFrames.mRestricted); - pf.set(displayFrames.mRestricted); + pf.left = df.left = of.left = cf.left = mRestrictedScreenLeft; + pf.top = df.top = of.top = cf.top = mRestrictedScreenTop; + pf.right = df.right = of.right = cf.right = mRestrictedScreenLeft + + mRestrictedScreenWidth; + pf.bottom = df.bottom = of.bottom = cf.bottom = mRestrictedScreenTop + + mRestrictedScreenHeight; } - applyStableConstraints(sysUiFl, fl, cf,displayFrames); + applyStableConstraints(sysUiFl, fl, cf); if (adjust != SOFT_INPUT_ADJUST_NOTHING) { - vf.set(displayFrames.mCurrent); + vf.left = mCurLeft; + vf.top = mCurTop; + vf.right = mCurRight; + vf.bottom = mCurBottom; } else { vf.set(cf); } } else if (attached != null) { - if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() - + "): attached to " + attached); + if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + + "): attached to " + attached); // A child window should be placed inside of the same visible // frame that its parent had. - setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf, - displayFrames); + setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf); } else { if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): normal window"); // Otherwise, a normal window must be placed inside the content // of all screen decorations. - if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_VOLUME_OVERLAY) { + if (attrs.type == TYPE_STATUS_BAR_PANEL || attrs.type == TYPE_VOLUME_OVERLAY) { // Status bar panels and the volume dialog are the only windows who can go on - // top of the status bar. They are protected by the STATUS_BAR_SERVICE - // permission, so they have the same privileges as the status bar itself. - cf.set(displayFrames.mRestricted); - of.set(displayFrames.mRestricted); - df.set(displayFrames.mRestricted); - pf.set(displayFrames.mRestricted); - } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) { + // top of the status bar. They are protected by the STATUS_BAR_SERVICE + // permission, so they have the same privileges as the status + // bar itself. + pf.left = df.left = of.left = cf.left = mRestrictedScreenLeft; + pf.top = df.top = of.top = cf.top = mRestrictedScreenTop; + pf.right = df.right = of.right = cf.right = mRestrictedScreenLeft + + mRestrictedScreenWidth; + pf.bottom = df.bottom = of.bottom = cf.bottom = mRestrictedScreenTop + + mRestrictedScreenHeight; + } else if (attrs.type == TYPE_TOAST || attrs.type == TYPE_SYSTEM_ALERT) { // These dialogs are stable to interim decor changes. - cf.set(displayFrames.mStable); - of.set(displayFrames.mStable); - df.set(displayFrames.mStable); - pf.set(displayFrames.mStable); + pf.left = df.left = of.left = cf.left = mStableLeft; + pf.top = df.top = of.top = cf.top = mStableTop; + pf.right = df.right = of.right = cf.right = mStableRight; + pf.bottom = df.bottom = of.bottom = cf.bottom = mStableBottom; } else { - pf.set(displayFrames.mContent); + pf.left = mContentLeft; + pf.top = mContentTop; + pf.right = mContentRight; + pf.bottom = mContentBottom; if (win.isVoiceInteraction()) { - cf.set(displayFrames.mVoiceContent); - of.set(displayFrames.mVoiceContent); - df.set(displayFrames.mVoiceContent); + df.left = of.left = cf.left = mVoiceContentLeft; + df.top = of.top = cf.top = mVoiceContentTop; + df.right = of.right = cf.right = mVoiceContentRight; + df.bottom = of.bottom = cf.bottom = mVoiceContentBottom; } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) { - cf.set(displayFrames.mDock); - of.set(displayFrames.mDock); - df.set(displayFrames.mDock); + df.left = of.left = cf.left = mDockLeft; + df.top = of.top = cf.top = mDockTop; + df.right = of.right = cf.right = mDockRight; + df.bottom = of.bottom = cf.bottom = mDockBottom; } else { - cf.set(displayFrames.mContent); - of.set(displayFrames.mContent); - df.set(displayFrames.mContent); + df.left = of.left = cf.left = mContentLeft; + df.top = of.top = cf.top = mContentTop; + df.right = of.right = cf.right = mContentRight; + df.bottom = of.bottom = cf.bottom = mContentBottom; } if (adjust != SOFT_INPUT_ADJUST_NOTHING) { - vf.set(displayFrames.mCurrent); + vf.left = mCurLeft; + vf.top = mCurTop; + vf.right = mCurRight; + vf.bottom = mCurBottom; } else { vf.set(cf); } @@ -5103,11 +5374,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it. // Also, we don't allow windows in multi-window mode to extend out of the screen. - if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR + if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && attrs.type != TYPE_SYSTEM_ERROR && !win.isInMultiWindowMode()) { df.left = df.top = -10000; df.right = df.bottom = 10000; - if (type != TYPE_WALLPAPER) { + if (attrs.type != TYPE_WALLPAPER) { of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000; of.right = of.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000; } @@ -5123,7 +5394,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { osf.set(cf.left, cf.top, cf.right, cf.bottom); int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources()); if (outset > 0) { - int rotation = displayFrames.mRotation; + int rotation = mDisplayRotation; if (rotation == Surface.ROTATION_0) { osf.bottom += outset; } else if (rotation == Surface.ROTATION_90) { @@ -5140,7 +5411,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle() + ": sim=#" + Integer.toHexString(sim) - + " attach=" + attached + " type=" + type + + " attach=" + attached + " type=" + attrs.type + String.format(" flags=0x%08x", fl) + " pf=" + pf.toShortString() + " df=" + df.toShortString() + " of=" + of.toShortString() @@ -5153,42 +5424,62 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Dock windows carve out the bottom of the screen, so normal windows // can't appear underneath them. - if (type == TYPE_INPUT_METHOD && win.isVisibleLw() + if (attrs.type == TYPE_INPUT_METHOD && win.isVisibleLw() && !win.getGivenInsetsPendingLw()) { setLastInputMethodWindowLw(null, null); - offsetInputMethodWindowLw(win, displayFrames); + offsetInputMethodWindowLw(win); } - if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw() + if (attrs.type == TYPE_VOICE_INTERACTION && win.isVisibleLw() && !win.getGivenInsetsPendingLw()) { - offsetVoiceInputWindowLw(win, displayFrames); + offsetVoiceInputWindowLw(win); } } - private void layoutWallpaper(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect cf) { - // The wallpaper has Real Ultimate Power, but we want to tell it about the overscan area. - df.set(displayFrames.mOverscan); - pf.set(displayFrames.mOverscan); - cf.set(displayFrames.mUnrestricted); - of.set(displayFrames.mUnrestricted); + private void layoutWallpaper(WindowState win, Rect pf, Rect df, Rect of, Rect cf) { + + // The wallpaper also has Real Ultimate Power, but we want to tell + // it about the overscan area. + pf.left = df.left = mOverscanScreenLeft; + pf.top = df.top = mOverscanScreenTop; + pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth; + pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight; + of.left = cf.left = mUnrestrictedScreenLeft; + of.top = cf.top = mUnrestrictedScreenTop; + of.right = cf.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth; + of.bottom = cf.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight; } - private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) { + private void offsetInputMethodWindowLw(WindowState win) { int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top); top += win.getGivenContentInsetsLw().top; - displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top); - displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top); + if (mContentBottom > top) { + mContentBottom = top; + } + if (mVoiceContentBottom > top) { + mVoiceContentBottom = top; + } top = win.getVisibleFrameLw().top; top += win.getGivenVisibleInsetsLw().top; - displayFrames.mCurrent.bottom = Math.min(displayFrames.mCurrent.bottom, top); + if (mCurBottom > top) { + mCurBottom = top; + } if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom=" - + displayFrames.mDock.bottom + " mContentBottom=" - + displayFrames.mContent.bottom + " mCurBottom=" + displayFrames.mCurrent.bottom); + + mDockBottom + " mContentBottom=" + + mContentBottom + " mCurBottom=" + mCurBottom); } - private void offsetVoiceInputWindowLw(WindowState win, DisplayFrames displayFrames) { + private void offsetVoiceInputWindowLw(WindowState win) { int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top); top += win.getGivenContentInsetsLw().top; - displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top); + if (mVoiceContentBottom > top) { + mVoiceContentBottom = top; + } + } + + /** {@inheritDoc} */ + @Override + public void finishLayoutLw() { + return; } /** {@inheritDoc} */ @@ -8040,6 +8331,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { mDismissImeOnBackKeyPressed = newValue; } + @Override + public int getInputMethodWindowVisibleHeightLw() { + return mDockBottom - mCurBottom; + } + @Override public void setCurrentUserLw(int newUserId) { mCurrentUserId = newUserId; @@ -8129,6 +8425,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); + new Rect(mStableLeft, mStableTop, mStableRight, mStableBottom).writeToProto(proto, + STABLE_BOUNDS); proto.end(token); } @@ -8234,6 +8532,58 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(" mScreenOnFully="); pw.println(mScreenOnFully); pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete); pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete); + pw.print(prefix); pw.print("mOverscanScreen=("); pw.print(mOverscanScreenLeft); + pw.print(","); pw.print(mOverscanScreenTop); + pw.print(") "); pw.print(mOverscanScreenWidth); + pw.print("x"); pw.println(mOverscanScreenHeight); + if (mOverscanLeft != 0 || mOverscanTop != 0 + || mOverscanRight != 0 || mOverscanBottom != 0) { + pw.print(prefix); pw.print("mOverscan left="); pw.print(mOverscanLeft); + pw.print(" top="); pw.print(mOverscanTop); + pw.print(" right="); pw.print(mOverscanRight); + pw.print(" bottom="); pw.println(mOverscanBottom); + } + pw.print(prefix); pw.print("mRestrictedOverscanScreen=("); + pw.print(mRestrictedOverscanScreenLeft); + pw.print(","); pw.print(mRestrictedOverscanScreenTop); + pw.print(") "); pw.print(mRestrictedOverscanScreenWidth); + pw.print("x"); pw.println(mRestrictedOverscanScreenHeight); + pw.print(prefix); pw.print("mUnrestrictedScreen=("); pw.print(mUnrestrictedScreenLeft); + pw.print(","); pw.print(mUnrestrictedScreenTop); + pw.print(") "); pw.print(mUnrestrictedScreenWidth); + pw.print("x"); pw.println(mUnrestrictedScreenHeight); + pw.print(prefix); pw.print("mRestrictedScreen=("); pw.print(mRestrictedScreenLeft); + pw.print(","); pw.print(mRestrictedScreenTop); + pw.print(") "); pw.print(mRestrictedScreenWidth); + pw.print("x"); pw.println(mRestrictedScreenHeight); + pw.print(prefix); pw.print("mStableFullscreen=("); pw.print(mStableFullscreenLeft); + pw.print(","); pw.print(mStableFullscreenTop); + pw.print(")-("); pw.print(mStableFullscreenRight); + pw.print(","); pw.print(mStableFullscreenBottom); pw.println(")"); + pw.print(prefix); pw.print("mStable=("); pw.print(mStableLeft); + pw.print(","); pw.print(mStableTop); + pw.print(")-("); pw.print(mStableRight); + pw.print(","); pw.print(mStableBottom); pw.println(")"); + pw.print(prefix); pw.print("mSystem=("); pw.print(mSystemLeft); + pw.print(","); pw.print(mSystemTop); + pw.print(")-("); pw.print(mSystemRight); + pw.print(","); pw.print(mSystemBottom); pw.println(")"); + pw.print(prefix); pw.print("mCur=("); pw.print(mCurLeft); + pw.print(","); pw.print(mCurTop); + pw.print(")-("); pw.print(mCurRight); + pw.print(","); pw.print(mCurBottom); pw.println(")"); + pw.print(prefix); pw.print("mContent=("); pw.print(mContentLeft); + pw.print(","); pw.print(mContentTop); + pw.print(")-("); pw.print(mContentRight); + pw.print(","); pw.print(mContentBottom); pw.println(")"); + pw.print(prefix); pw.print("mVoiceContent=("); pw.print(mVoiceContentLeft); + pw.print(","); pw.print(mVoiceContentTop); + pw.print(")-("); pw.print(mVoiceContentRight); + pw.print(","); pw.print(mVoiceContentBottom); pw.println(")"); + pw.print(prefix); pw.print("mDock=("); pw.print(mDockLeft); + pw.print(","); pw.print(mDockTop); + pw.print(")-("); pw.print(mDockRight); + pw.print(","); pw.print(mDockBottom); pw.println(")"); pw.print(prefix); pw.print("mDockLayer="); pw.print(mDockLayer); pw.print(" mStatusBarLayer="); pw.println(mStatusBarLayer); pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c4b810f7d30b6..67d62e103f178 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -104,7 +104,6 @@ import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW; import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE; import static com.android.server.wm.proto.DisplayProto.ABOVE_APP_WINDOWS; import static com.android.server.wm.proto.DisplayProto.BELOW_APP_WINDOWS; -import static com.android.server.wm.proto.DisplayProto.DISPLAY_FRAMES; import static com.android.server.wm.proto.DisplayProto.DISPLAY_INFO; import static com.android.server.wm.proto.DisplayProto.DOCKED_STACK_DIVIDER_CONTROLLER; import static com.android.server.wm.proto.DisplayProto.DPI; @@ -148,7 +147,6 @@ import android.view.WindowManagerPolicy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ToBooleanFunction; import com.android.internal.view.IInputMethodClient; -import android.view.DisplayFrames; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -224,8 +222,6 @@ class DisplayContent extends WindowContainer= " + second, first >= second); } - private void finishActivity(Activity a) { - if (a == null) { - return; - } - a.finish(); + private Activity launchActivity(ActivityTestRule activityRule) { + final Activity activity = activityRule.launchActivity(null); waitForIdle(); + return activity; + } + + private void finishActivity(ActivityTestRule activityRule) { + final Activity activity = activityRule.getActivity(); + if (activity != null) { + activity.finish(); + } } private void waitForIdle() { mInstrumentation.waitForIdleSync(); } - private Activity startActivityOnDisplay(Class cls, int displayId) { - final Intent intent = new Intent(mContext, cls); - intent.addFlags(FLAG_ACTIVITY_NEW_TASK); - final ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchDisplayId(displayId); - final Activity activity = mInstrumentation.startActivitySync(intent, options.toBundle()); - waitForIdle(); - - assertEquals(displayId, activity.getDisplay().getDisplayId()); - return activity; - } - - private Pair createDisplay() { - final DisplayManager dm = mContext.getSystemService(DisplayManager.class); - final DisplayInfo displayInfo = new DisplayInfo(); - final Display defaultDisplay = dm.getDisplay(DEFAULT_DISPLAY); - defaultDisplay.getDisplayInfo(displayInfo); - final String name = "ScreenDecorWindowTests"; - int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; - - final ImageReader imageReader = ImageReader.newInstance( - displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2); - - final VirtualDisplay display = dm.createVirtualDisplay(name, displayInfo.logicalWidth, - displayInfo.logicalHeight, displayInfo.logicalDensityDpi, imageReader.getSurface(), - flags); - - return Pair.create(display, imageReader); - } - public static class TestActivity extends Activity { } } diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index 53d0bfbb5cf7f..5134c26ddf911 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -132,6 +132,11 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } + @Override + public void setDisplayOverscan(Display display, int left, int top, int right, int bottom) { + + } + @Override public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) { return 0; @@ -284,11 +289,40 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { return null; } + @Override + public void beginLayoutLw(int displayId, int displayWidth, int displayHeight, + int displayRotation, int uiMode) { + + } + @Override public int getSystemDecorLayerLw() { return 0; } + @Override + public void getContentRectLw(Rect r) { + + } + + @Override + public void layoutWindowLw(WindowState win, + WindowState attached) { + + } + + @Override + public boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds, + int displayRotation, int displayWidth, int displayHeight, Rect outContentInsets, + Rect outStableInsets, Rect outOutsets) { + return false; + } + + @Override + public void finishLayoutLw() { + + } + @Override public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) { @@ -547,6 +581,11 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } + @Override + public int getInputMethodWindowVisibleHeightLw() { + return 0; + } + @Override public void setCurrentUserLw(int newUserId) { From 3d09ba8627862b506b3c7eb3f241d33209c4a24f Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Tue, 14 Nov 2017 13:42:42 -0500 Subject: [PATCH 016/226] Disable skia shader cache Disable skia shader cache, which is causing image rendering issues. Test: Ran CNN app and images are OK. Bug: 69264347 Change-Id: Ie81f3398074f28ac1670333f1fd3c95267b2beb3 (cherry picked from commit 01b439475ccd965c37f4ae194c0dc284628c7635) --- libs/hwui/renderthread/CacheManager.cpp | 3 --- libs/hwui/renderthread/RenderThread.cpp | 2 -- 2 files changed, 5 deletions(-) diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index c22364b4a5699..a33b2874e7a50 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -18,7 +18,6 @@ #include "Layer.h" #include "RenderThread.h" -#include "pipeline/skia/ShaderCache.h" #include "renderstate/RenderState.h" #include @@ -128,8 +127,6 @@ void CacheManager::configureContext(GrContextOptions* contextOptions) { } contextOptions->fExecutor = mTaskProcessor.get(); } - - contextOptions->fPersistentCache = &skiapipeline::ShaderCache::get(); } void CacheManager::trimMemory(TrimMemoryMode mode) { diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 3e2eeee2bcf53..05a9b75b45042 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -16,7 +16,6 @@ #include "RenderThread.h" -#include "pipeline/skia/ShaderCache.h" #include "CanvasContext.h" #include "EglManager.h" #include "OpenGLReadback.h" @@ -106,7 +105,6 @@ void RenderThread::initThreadLocals() { mRenderState = new RenderState(*this); mVkManager = new VulkanManager(*this); mCacheManager = new CacheManager(mDisplayInfo); - uirenderer::skiapipeline::ShaderCache::get().initShaderDiskCache(); } void RenderThread::dumpGraphicsMemory(int fd) { From 18e657610cf8f99fd5399cd21dadc2a809eb72a9 Mon Sep 17 00:00:00 2001 From: Cassie Han Date: Wed, 15 Nov 2017 17:27:31 +0000 Subject: [PATCH 017/226] Revert "Allow unknown mcc/mnc when constructing a CellIdentity from Parcel." Bug: 69349963 Bug: 69096589 Bug: 63984327 This reverts commit 993203c7e0d397f193b80c01774b4be925242dae. Change-Id: I52839aa5991541399651904587aa342a858a5915 (cherry picked from commit 2200da1cdc292507947eb198a4252f95b1dfb0fc) --- telephony/java/android/telephony/CellIdentityGsm.java | 9 ++++----- telephony/java/android/telephony/CellIdentityLte.java | 9 ++++----- .../java/android/telephony/CellIdentityWcdma.java | 11 +++++------ 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java index 4d7c71febd6f5..6276626afae26 100644 --- a/telephony/java/android/telephony/CellIdentityGsm.java +++ b/telephony/java/android/telephony/CellIdentityGsm.java @@ -115,11 +115,10 @@ public final class CellIdentityGsm implements Parcelable { // for inbound parcels mBsic = (bsic == 0xFF) ? Integer.MAX_VALUE : bsic; - // Only allow INT_MAX if unknown string mcc/mnc if (mccStr == null || mccStr.matches("^[0-9]{3}$")) { mMccStr = mccStr; - } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) { - // If the mccStr is empty or unknown, set it as null. + } else if (mccStr.isEmpty()) { + // If the mccStr parsed from Parcel is empty, set it as null. mMccStr = null; } else { throw new IllegalArgumentException("invalid MCC format"); @@ -127,8 +126,8 @@ public final class CellIdentityGsm implements Parcelable { if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) { mMncStr = mncStr; - } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) { - // If the mncStr is empty or unknown, set it as null. + } else if (mncStr.isEmpty()) { + // If the mncStr parsed from Parcel is empty, set it as null. mMncStr = null; } else { throw new IllegalArgumentException("invalid MNC format"); diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java index fd837fc0ac26f..74d2966b25bca 100644 --- a/telephony/java/android/telephony/CellIdentityLte.java +++ b/telephony/java/android/telephony/CellIdentityLte.java @@ -114,11 +114,10 @@ public final class CellIdentityLte implements Parcelable { mTac = tac; mEarfcn = earfcn; - // Only allow INT_MAX if unknown string mcc/mnc if (mccStr == null || mccStr.matches("^[0-9]{3}$")) { mMccStr = mccStr; - } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) { - // If the mccStr is empty or unknown, set it as null. + } else if (mccStr.isEmpty()) { + // If the mccStr parsed from Parcel is empty, set it as null. mMccStr = null; } else { throw new IllegalArgumentException("invalid MCC format"); @@ -126,8 +125,8 @@ public final class CellIdentityLte implements Parcelable { if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) { mMncStr = mncStr; - } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) { - // If the mncStr is empty or unknown, set it as null. + } else if (mncStr.isEmpty()) { + // If the mncStr parsed from Parcel is empty, set it as null. mMncStr = null; } else { throw new IllegalArgumentException("invalid MNC format"); diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java index 1597245c6fa21..51b11aa82b9a3 100644 --- a/telephony/java/android/telephony/CellIdentityWcdma.java +++ b/telephony/java/android/telephony/CellIdentityWcdma.java @@ -114,11 +114,10 @@ public final class CellIdentityWcdma implements Parcelable { mPsc = psc; mUarfcn = uarfcn; - // Only allow INT_MAX if unknown string mcc/mnc if (mccStr == null || mccStr.matches("^[0-9]{3}$")) { mMccStr = mccStr; - } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) { - // If the mccStr is empty or unknown, set it as null. + } else if (mccStr.isEmpty()) { + // If the mccStr parsed from Parcel is empty, set it as null. mMccStr = null; } else { throw new IllegalArgumentException("invalid MCC format"); @@ -126,8 +125,8 @@ public final class CellIdentityWcdma implements Parcelable { if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) { mMncStr = mncStr; - } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) { - // If the mncStr is empty or unknown, set it as null. + } else if (mncStr.isEmpty()) { + // If the mncStr parsed from Parcel is empty, set it as null. mMncStr = null; } else { throw new IllegalArgumentException("invalid MNC format"); @@ -136,7 +135,7 @@ public final class CellIdentityWcdma implements Parcelable { mAlphaLong = alphal; mAlphaShort = alphas; } - + private CellIdentityWcdma(CellIdentityWcdma cid) { this(cid.mLac, cid.mCid, cid.mPsc, cid.mUarfcn, cid.mMccStr, cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort); From 7e4af7a5505c858e8c4f70e1e482f1599340eccf Mon Sep 17 00:00:00 2001 From: John Reck Date: Thu, 16 Nov 2017 13:44:35 -0800 Subject: [PATCH 018/226] Make ripples silky smooth * Updates press state ripple to match UX spec * Makes it ungodly silky smooth LIKE BUTTAH * Update hover & focus states to be closer to UX spec, still needs a final pass. Bug: 63635160 Test: Clicked on a bunch of stuff Change-Id: I162ab9d8d669002f2ae511f93b5d9fe67f99c533 (cherry picked from commit 0c453ccb87e0b5a4f4b318df01700c9a9a0da545) --- .../java/android/view/RenderNodeAnimator.java | 10 +- .../graphics/drawable/RippleBackground.java | 137 ++----- .../graphics/drawable/RippleComponent.java | 244 +----------- .../graphics/drawable/RippleDrawable.java | 134 +++---- .../graphics/drawable/RippleForeground.java | 352 +++++++++++------- 5 files changed, 317 insertions(+), 560 deletions(-) diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index 95150409514db..c4a7160117652 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -19,7 +19,6 @@ package android.view; import android.animation.Animator; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; -import android.graphics.Canvas; import android.graphics.CanvasProperty; import android.graphics.Paint; import android.util.SparseIntArray; @@ -281,12 +280,9 @@ public class RenderNodeAnimator extends Animator { setTarget(mViewTarget.mRenderNode); } - public void setTarget(Canvas canvas) { - if (!(canvas instanceof DisplayListCanvas)) { - throw new IllegalArgumentException("Not a GLES20RecordingCanvas"); - } - final DisplayListCanvas recordingCanvas = (DisplayListCanvas) canvas; - setTarget(recordingCanvas.mNode); + /** Sets the animation target to the owning view of the DisplayListCanvas */ + public void setTarget(DisplayListCanvas canvas) { + setTarget(canvas.mNode); } private void setTarget(RenderNode node) { diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java index 3bf4f90257b73..dea194e4ffde6 100644 --- a/graphics/java/android/graphics/drawable/RippleBackground.java +++ b/graphics/java/android/graphics/drawable/RippleBackground.java @@ -36,138 +36,69 @@ class RippleBackground extends RippleComponent { private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); - private static final int OPACITY_ENTER_DURATION = 600; - private static final int OPACITY_ENTER_DURATION_FAST = 120; - private static final int OPACITY_EXIT_DURATION = 480; + private static final int OPACITY_DURATION = 80; - // Hardware rendering properties. - private CanvasProperty mPropPaint; - private CanvasProperty mPropRadius; - private CanvasProperty mPropX; - private CanvasProperty mPropY; + private ObjectAnimator mAnimator; - // Software rendering properties. private float mOpacity = 0; /** Whether this ripple is bounded. */ private boolean mIsBounded; - public RippleBackground(RippleDrawable owner, Rect bounds, boolean isBounded, - boolean forceSoftware) { - super(owner, bounds, forceSoftware); + private boolean mFocused = false; + private boolean mHovered = false; + + public RippleBackground(RippleDrawable owner, Rect bounds, boolean isBounded) { + super(owner, bounds); mIsBounded = isBounded; } public boolean isVisible() { - return mOpacity > 0 || isHardwareAnimating(); + return mOpacity > 0; } - @Override - protected boolean drawSoftware(Canvas c, Paint p) { - boolean hasContent = false; - + public void draw(Canvas c, Paint p) { final int origAlpha = p.getAlpha(); - final int alpha = (int) (origAlpha * mOpacity + 0.5f); + final int alpha = Math.min((int) (origAlpha * mOpacity + 0.5f), 255); if (alpha > 0) { p.setAlpha(alpha); c.drawCircle(0, 0, mTargetRadius, p); p.setAlpha(origAlpha); - hasContent = true; } - - return hasContent; } - @Override - protected boolean drawHardware(DisplayListCanvas c) { - c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint); - return true; - } - - @Override - protected Animator createSoftwareEnter(boolean fast) { - // Linear enter based on current opacity. - final int maxDuration = fast ? OPACITY_ENTER_DURATION_FAST : OPACITY_ENTER_DURATION; - final int duration = (int) ((1 - mOpacity) * maxDuration); - - final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1); - opacity.setAutoCancel(true); - opacity.setDuration(duration); - opacity.setInterpolator(LINEAR_INTERPOLATOR); - - return opacity; - } - - @Override - protected Animator createSoftwareExit() { - final AnimatorSet set = new AnimatorSet(); - - // Linear exit after enter is completed. - final ObjectAnimator exit = ObjectAnimator.ofFloat(this, OPACITY, 0); - exit.setInterpolator(LINEAR_INTERPOLATOR); - exit.setDuration(OPACITY_EXIT_DURATION); - exit.setAutoCancel(true); - - final AnimatorSet.Builder builder = set.play(exit); - - // Linear "fast" enter based on current opacity. - final int fastEnterDuration = mIsBounded ? - (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0; - if (fastEnterDuration > 0) { - final ObjectAnimator enter = ObjectAnimator.ofFloat(this, OPACITY, 1); - enter.setInterpolator(LINEAR_INTERPOLATOR); - enter.setDuration(fastEnterDuration); - enter.setAutoCancel(true); - - builder.after(enter); + public void setState(boolean focused, boolean hovered, boolean animateChanged) { + if (mHovered != hovered || mFocused != focused) { + mHovered = hovered; + mFocused = focused; + onStateChanged(animateChanged); } - - return set; } - @Override - protected RenderNodeAnimatorSet createHardwareExit(Paint p) { - final RenderNodeAnimatorSet set = new RenderNodeAnimatorSet(); - - final int targetAlpha = p.getAlpha(); - final int currentAlpha = (int) (mOpacity * targetAlpha + 0.5f); - p.setAlpha(currentAlpha); - - mPropPaint = CanvasProperty.createPaint(p); - mPropRadius = CanvasProperty.createFloat(mTargetRadius); - mPropX = CanvasProperty.createFloat(0); - mPropY = CanvasProperty.createFloat(0); - - final int fastEnterDuration = mIsBounded ? - (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0; - - // Linear exit after enter is completed. - final RenderNodeAnimator exit = new RenderNodeAnimator( - mPropPaint, RenderNodeAnimator.PAINT_ALPHA, 0); - exit.setInterpolator(LINEAR_INTERPOLATOR); - exit.setDuration(OPACITY_EXIT_DURATION); - if (fastEnterDuration > 0) { - exit.setStartDelay(fastEnterDuration); - exit.setStartValue(targetAlpha); + private void onStateChanged(boolean animateChanged) { + float newOpacity = 0.0f; + if (mHovered) newOpacity += 1.0f; + if (mFocused) newOpacity += 1.0f; + if (mAnimator != null) { + mAnimator.cancel(); + mAnimator = null; } - set.add(exit); - - // Linear "fast" enter based on current opacity. - if (fastEnterDuration > 0) { - final RenderNodeAnimator enter = new RenderNodeAnimator( - mPropPaint, RenderNodeAnimator.PAINT_ALPHA, targetAlpha); - enter.setInterpolator(LINEAR_INTERPOLATOR); - enter.setDuration(fastEnterDuration); - set.add(enter); + if (animateChanged) { + mAnimator = ObjectAnimator.ofFloat(this, OPACITY, newOpacity); + mAnimator.setDuration(OPACITY_DURATION); + mAnimator.setInterpolator(LINEAR_INTERPOLATOR); + mAnimator.start(); + } else { + mOpacity = newOpacity; } - - return set; } - @Override - protected void jumpValuesToExit() { - mOpacity = 0; + public void jumpToFinal() { + if (mAnimator != null) { + mAnimator.end(); + mAnimator = null; + } } private static abstract class BackgroundProperty extends FloatProperty { diff --git a/graphics/java/android/graphics/drawable/RippleComponent.java b/graphics/java/android/graphics/drawable/RippleComponent.java index e83513c644dbc..0e38826eb34be 100644 --- a/graphics/java/android/graphics/drawable/RippleComponent.java +++ b/graphics/java/android/graphics/drawable/RippleComponent.java @@ -27,23 +27,14 @@ import android.view.RenderNodeAnimator; import java.util.ArrayList; /** - * Abstract class that handles hardware/software hand-off and lifecycle for - * animated ripple foreground and background components. + * Abstract class that handles size & positioning common to the ripple & focus states. */ abstract class RippleComponent { - private final RippleDrawable mOwner; + protected final RippleDrawable mOwner; /** Bounds used for computing max radius. May be modified by the owner. */ protected final Rect mBounds; - /** Whether we can use hardware acceleration for the exit animation. */ - private boolean mHasDisplayListCanvas; - - private boolean mHasPendingHardwareAnimator; - private RenderNodeAnimatorSet mHardwareAnimator; - - private Animator mSoftwareAnimator; - /** Whether we have an explicit maximum radius. */ private boolean mHasMaxRadius; @@ -53,16 +44,9 @@ abstract class RippleComponent { /** Screen density used to adjust pixel-based constants. */ protected float mDensityScale; - /** - * If set, force all ripple animations to not run on RenderThread, even if it would be - * available. - */ - private final boolean mForceSoftware; - - public RippleComponent(RippleDrawable owner, Rect bounds, boolean forceSoftware) { + public RippleComponent(RippleDrawable owner, Rect bounds) { mOwner = owner; mBounds = bounds; - mForceSoftware = forceSoftware; } public void onBoundsChange() { @@ -91,89 +75,6 @@ abstract class RippleComponent { return (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight); } - /** - * Starts a ripple enter animation. - * - * @param fast whether the ripple should enter quickly - */ - public final void enter(boolean fast) { - cancel(); - - mSoftwareAnimator = createSoftwareEnter(fast); - - if (mSoftwareAnimator != null) { - mSoftwareAnimator.start(); - } - } - - /** - * Starts a ripple exit animation. - */ - public final void exit() { - cancel(); - - if (mHasDisplayListCanvas) { - // We don't have access to a canvas here, but we expect one on the - // next frame. We'll start the render thread animation then. - mHasPendingHardwareAnimator = true; - - // Request another frame. - invalidateSelf(); - } else { - mSoftwareAnimator = createSoftwareExit(); - mSoftwareAnimator.start(); - } - } - - /** - * Cancels all animations. Software animation values are left in the - * current state, while hardware animation values jump to the end state. - */ - public void cancel() { - cancelSoftwareAnimations(); - endHardwareAnimations(); - } - - /** - * Ends all animations, jumping values to the end state. - */ - public void end() { - endSoftwareAnimations(); - endHardwareAnimations(); - } - - /** - * Draws the ripple to the canvas, inheriting the paint's color and alpha - * properties. - * - * @param c the canvas to which the ripple should be drawn - * @param p the paint used to draw the ripple - * @return {@code true} if something was drawn, {@code false} otherwise - */ - public boolean draw(Canvas c, Paint p) { - final boolean hasDisplayListCanvas = !mForceSoftware && c.isHardwareAccelerated() - && c instanceof DisplayListCanvas; - if (mHasDisplayListCanvas != hasDisplayListCanvas) { - mHasDisplayListCanvas = hasDisplayListCanvas; - - if (!hasDisplayListCanvas) { - // We've switched from hardware to non-hardware mode. Panic. - endHardwareAnimations(); - } - } - - if (hasDisplayListCanvas) { - final DisplayListCanvas hw = (DisplayListCanvas) c; - startPendingAnimation(hw, p); - - if (mHardwareAnimator != null) { - return drawHardware(hw); - } - } - - return drawSoftware(c, p); - } - /** * Populates {@code bounds} with the maximum drawing bounds of the ripple * relative to its center. The resulting bounds should be translated into @@ -186,77 +87,10 @@ abstract class RippleComponent { bounds.set(-r, -r, r, r); } - /** - * Starts the pending hardware animation, if available. - * - * @param hw hardware canvas on which the animation should draw - * @param p paint whose properties the hardware canvas should use - */ - private void startPendingAnimation(DisplayListCanvas hw, Paint p) { - if (mHasPendingHardwareAnimator) { - mHasPendingHardwareAnimator = false; - - mHardwareAnimator = createHardwareExit(new Paint(p)); - mHardwareAnimator.start(hw); - - // Preemptively jump the software values to the end state now that - // the hardware exit has read whatever values it needs. - jumpValuesToExit(); - } - } - - /** - * Cancels any current software animations, leaving the values in their - * current state. - */ - private void cancelSoftwareAnimations() { - if (mSoftwareAnimator != null) { - mSoftwareAnimator.cancel(); - mSoftwareAnimator = null; - } - } - - /** - * Ends any current software animations, jumping the values to their end - * state. - */ - private void endSoftwareAnimations() { - if (mSoftwareAnimator != null) { - mSoftwareAnimator.end(); - mSoftwareAnimator = null; - } - } - - /** - * Ends any pending or current hardware animations. - *

- * Hardware animations can't synchronize values back to the software - * thread, so there is no "cancel" equivalent. - */ - private void endHardwareAnimations() { - if (mHardwareAnimator != null) { - mHardwareAnimator.end(); - mHardwareAnimator = null; - } - - if (mHasPendingHardwareAnimator) { - mHasPendingHardwareAnimator = false; - - // Manually jump values to their exited state. Normally we'd do that - // later when starting the hardware exit, but we're aborting early. - jumpValuesToExit(); - } - } - protected final void invalidateSelf() { mOwner.invalidateSelf(false); } - protected final boolean isHardwareAnimating() { - return mHardwareAnimator != null && mHardwareAnimator.isRunning() - || mHasPendingHardwareAnimator; - } - protected final void onHotspotBoundsChanged() { if (!mHasMaxRadius) { final float halfWidth = mBounds.width() / 2.0f; @@ -276,76 +110,4 @@ abstract class RippleComponent { protected void onTargetRadiusChanged(float targetRadius) { // Stub. } - - protected abstract Animator createSoftwareEnter(boolean fast); - - protected abstract Animator createSoftwareExit(); - - protected abstract RenderNodeAnimatorSet createHardwareExit(Paint p); - - protected abstract boolean drawHardware(DisplayListCanvas c); - - protected abstract boolean drawSoftware(Canvas c, Paint p); - - /** - * Called when the hardware exit is cancelled. Jumps software values to end - * state to ensure that software and hardware values are synchronized. - */ - protected abstract void jumpValuesToExit(); - - public static class RenderNodeAnimatorSet { - private final ArrayList mAnimators = new ArrayList<>(); - - public void add(RenderNodeAnimator anim) { - mAnimators.add(anim); - } - - public void clear() { - mAnimators.clear(); - } - - public void start(DisplayListCanvas target) { - if (target == null) { - throw new IllegalArgumentException("Hardware canvas must be non-null"); - } - - final ArrayList animators = mAnimators; - final int N = animators.size(); - for (int i = 0; i < N; i++) { - final RenderNodeAnimator anim = animators.get(i); - anim.setTarget(target); - anim.start(); - } - } - - public void cancel() { - final ArrayList animators = mAnimators; - final int N = animators.size(); - for (int i = 0; i < N; i++) { - final RenderNodeAnimator anim = animators.get(i); - anim.cancel(); - } - } - - public void end() { - final ArrayList animators = mAnimators; - final int N = animators.size(); - for (int i = 0; i < N; i++) { - final RenderNodeAnimator anim = animators.get(i); - anim.end(); - } - } - - public boolean isRunning() { - final ArrayList animators = mAnimators; - final int N = animators.size(); - for (int i = 0; i < N; i++) { - final RenderNodeAnimator anim = animators.get(i); - if (anim.isRunning()) { - return true; - } - } - return false; - } - } } diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index 1727eca544a9f..8b185f2b8903d 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -16,11 +16,6 @@ package android.graphics.drawable; -import com.android.internal.R; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.ActivityInfo.Config; @@ -42,6 +37,11 @@ import android.graphics.Rect; import android.graphics.Shader; import android.util.AttributeSet; +import com.android.internal.R; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import java.io.IOException; import java.util.Arrays; @@ -135,9 +135,6 @@ public class RippleDrawable extends LayerDrawable { private PorterDuffColorFilter mMaskColorFilter; private boolean mHasValidMask; - /** Whether we expect to draw a background when visible. */ - private boolean mBackgroundActive; - /** The current ripple. May be actively animating or pending entry. */ private RippleForeground mRipple; @@ -217,7 +214,7 @@ public class RippleDrawable extends LayerDrawable { } if (mBackground != null) { - mBackground.end(); + mBackground.jumpToFinal(); } cancelExitingRipples(); @@ -266,9 +263,9 @@ public class RippleDrawable extends LayerDrawable { } } - setRippleActive(focused || (enabled && pressed)); + setRippleActive(enabled && pressed); - setBackgroundActive(hovered, hovered); + setBackgroundActive(hovered, focused); return changed; } @@ -283,14 +280,13 @@ public class RippleDrawable extends LayerDrawable { } } - private void setBackgroundActive(boolean active, boolean focused) { - if (mBackgroundActive != active) { - mBackgroundActive = active; - if (active) { - tryBackgroundEnter(focused); - } else { - tryBackgroundExit(); - } + private void setBackgroundActive(boolean hovered, boolean focused) { + if (mBackground == null && (hovered || focused)) { + mBackground = new RippleBackground(this, mHotspotBounds, isBounded()); + mBackground.setup(mState.mMaxRadius, mDensity); + } + if (mBackground != null) { + mBackground.setState(focused, hovered, true); } } @@ -327,10 +323,6 @@ public class RippleDrawable extends LayerDrawable { tryRippleEnter(); } - if (mBackgroundActive) { - tryBackgroundEnter(false); - } - // Skip animations, just show the correct final states. jumpToCurrentState(); } @@ -545,26 +537,6 @@ public class RippleDrawable extends LayerDrawable { } } - /** - * Creates an active hotspot at the specified location. - */ - private void tryBackgroundEnter(boolean focused) { - if (mBackground == null) { - final boolean isBounded = isBounded(); - mBackground = new RippleBackground(this, mHotspotBounds, isBounded, mForceSoftware); - } - - mBackground.setup(mState.mMaxRadius, mDensity); - mBackground.enter(focused); - } - - private void tryBackgroundExit() { - if (mBackground != null) { - // Don't null out the background, we need it to draw! - mBackground.exit(); - } - } - /** * Attempts to start an enter animation for the active hotspot. Fails if * there are too many animating ripples. @@ -593,7 +565,7 @@ public class RippleDrawable extends LayerDrawable { } mRipple.setup(mState.mMaxRadius, mDensity); - mRipple.enter(false); + mRipple.enter(); } /** @@ -623,9 +595,7 @@ public class RippleDrawable extends LayerDrawable { } if (mBackground != null) { - mBackground.end(); - mBackground = null; - mBackgroundActive = false; + mBackground.setState(false, false, false); } cancelExitingRipples(); @@ -858,38 +828,8 @@ public class RippleDrawable extends LayerDrawable { final float y = mHotspotBounds.exactCenterY(); canvas.translate(x, y); - updateMaskShaderIfNeeded(); - - // Position the shader to account for canvas translation. - if (mMaskShader != null) { - final Rect bounds = getBounds(); - mMaskMatrix.setTranslate(bounds.left - x, bounds.top - y); - mMaskShader.setLocalMatrix(mMaskMatrix); - } - - // Grab the color for the current state and cut the alpha channel in - // half so that the ripple and background together yield full alpha. - final int color = mState.mColor.getColorForState(getState(), Color.BLACK); - final int halfAlpha = (Color.alpha(color) / 2) << 24; final Paint p = getRipplePaint(); - if (mMaskColorFilter != null) { - // The ripple timing depends on the paint's alpha value, so we need - // to push just the alpha channel into the paint and let the filter - // handle the full-alpha color. - final int fullAlphaColor = color | (0xFF << 24); - mMaskColorFilter.setColor(fullAlphaColor); - - p.setColor(halfAlpha); - p.setColorFilter(mMaskColorFilter); - p.setShader(mMaskShader); - } else { - final int halfAlphaColor = (color & 0xFFFFFF) | halfAlpha; - p.setColor(halfAlphaColor); - p.setColorFilter(null); - p.setShader(null); - } - if (background != null && background.isVisible()) { background.draw(canvas, p); } @@ -912,13 +852,49 @@ public class RippleDrawable extends LayerDrawable { mMask.draw(canvas); } - private Paint getRipplePaint() { + Paint getRipplePaint() { if (mRipplePaint == null) { mRipplePaint = new Paint(); mRipplePaint.setAntiAlias(true); mRipplePaint.setStyle(Paint.Style.FILL); } - return mRipplePaint; + + final float x = mHotspotBounds.exactCenterX(); + final float y = mHotspotBounds.exactCenterY(); + + updateMaskShaderIfNeeded(); + + // Position the shader to account for canvas translation. + if (mMaskShader != null) { + final Rect bounds = getBounds(); + mMaskMatrix.setTranslate(bounds.left - x, bounds.top - y); + mMaskShader.setLocalMatrix(mMaskMatrix); + } + + // Grab the color for the current state and cut the alpha channel in + // half so that the ripple and background together yield full alpha. + final int color = mState.mColor.getColorForState(getState(), Color.BLACK); + final int halfAlpha = (Color.alpha(color) / 2) << 24; + final Paint p = mRipplePaint; + + if (mMaskColorFilter != null) { + // The ripple timing depends on the paint's alpha value, so we need + // to push just the alpha channel into the paint and let the filter + // handle the full-alpha color. + final int fullAlphaColor = color | (0xFF << 24); + mMaskColorFilter.setColor(fullAlphaColor); + + p.setColor(halfAlpha); + p.setColorFilter(mMaskColorFilter); + p.setShader(mMaskShader); + } else { + final int halfAlphaColor = (color & 0xFFFFFF) | halfAlpha; + p.setColor(halfAlphaColor); + p.setColorFilter(null); + p.setShader(null); + } + + return p; } @Override diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java index a675eaf852977..0b5020cbe55c2 100644 --- a/graphics/java/android/graphics/drawable/RippleForeground.java +++ b/graphics/java/android/graphics/drawable/RippleForeground.java @@ -18,7 +18,6 @@ package android.graphics.drawable; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.graphics.Canvas; @@ -29,8 +28,11 @@ import android.util.FloatProperty; import android.util.MathUtils; import android.view.DisplayListCanvas; import android.view.RenderNodeAnimator; +import android.view.animation.AnimationUtils; import android.view.animation.LinearInterpolator; +import java.util.ArrayList; + /** * Draws a ripple foreground. */ @@ -40,7 +42,7 @@ class RippleForeground extends RippleComponent { 400f, 1.4f, 0); // Pixel-based accelerations and velocities. - private static final float WAVE_TOUCH_DOWN_ACCELERATION = 1024; + private static final float WAVE_TOUCH_DOWN_ACCELERATION = 2048; private static final float WAVE_OPACITY_DECAY_VELOCITY = 3; // Bounded ripple animation properties. @@ -49,8 +51,9 @@ class RippleForeground extends RippleComponent { private static final int BOUNDED_OPACITY_EXIT_DURATION = 400; private static final float MAX_BOUNDED_RADIUS = 350; - private static final int RIPPLE_ENTER_DELAY = 80; - private static final int OPACITY_ENTER_DURATION_FAST = 120; + private static final int OPACITY_ENTER_DURATION = 75; + private static final int OPACITY_EXIT_DURATION = 150; + private static final int OPACITY_HOLD_DURATION = OPACITY_ENTER_DURATION + 150; // Parent-relative values for starting position. private float mStartingX; @@ -72,7 +75,7 @@ class RippleForeground extends RippleComponent { private float mBoundedRadius = 0; // Software rendering properties. - private float mOpacity = 1; + private float mOpacity = 0; // Values used to tween between the start and end positions. private float mTweenRadius = 0; @@ -82,6 +85,22 @@ class RippleForeground extends RippleComponent { /** Whether this ripple has finished its exit animation. */ private boolean mHasFinishedExit; + /** Whether we can use hardware acceleration for the exit animation. */ + private boolean mUsingProperties; + + private long mEnterStartedAtMillis; + + private ArrayList mPendingHwAnimators = new ArrayList<>(); + private ArrayList mRunningHwAnimators = new ArrayList<>(); + + private ArrayList mRunningSwAnimators = new ArrayList<>(); + + /** + * If set, force all ripple animations to not run on RenderThread, even if it would be + * available. + */ + private final boolean mForceSoftware; + /** * If we have a bound, don't start from 0. Start from 60% of the max out of width and height. */ @@ -89,8 +108,9 @@ class RippleForeground extends RippleComponent { public RippleForeground(RippleDrawable owner, Rect bounds, float startingX, float startingY, boolean isBounded, boolean forceSoftware) { - super(owner, bounds, forceSoftware); + super(owner, bounds); + mForceSoftware = forceSoftware; mStartingX = startingX; mStartingY = startingY; @@ -109,10 +129,7 @@ class RippleForeground extends RippleComponent { clampStartingPosition(); } - @Override - protected boolean drawSoftware(Canvas c, Paint p) { - boolean hasContent = false; - + private void drawSoftware(Canvas c, Paint p) { final int origAlpha = p.getAlpha(); final int alpha = (int) (origAlpha * mOpacity + 0.5f); final float radius = getCurrentRadius(); @@ -122,16 +139,51 @@ class RippleForeground extends RippleComponent { p.setAlpha(alpha); c.drawCircle(x, y, radius, p); p.setAlpha(origAlpha); - hasContent = true; } - - return hasContent; } - @Override - protected boolean drawHardware(DisplayListCanvas c) { - c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint); - return true; + private void startPending(DisplayListCanvas c) { + if (!mPendingHwAnimators.isEmpty()) { + for (int i = 0; i < mPendingHwAnimators.size(); i++) { + RenderNodeAnimator animator = mPendingHwAnimators.get(i); + animator.setTarget(c); + animator.start(); + mRunningHwAnimators.add(animator); + } + mPendingHwAnimators.clear(); + } + } + + private void pruneHwFinished() { + if (!mRunningHwAnimators.isEmpty()) { + for (int i = mRunningHwAnimators.size() - 1; i >= 0; i--) { + if (!mRunningHwAnimators.get(i).isRunning()) { + mRunningHwAnimators.remove(i); + } + } + } + } + + private void pruneSwFinished() { + if (!mRunningSwAnimators.isEmpty()) { + for (int i = mRunningSwAnimators.size() - 1; i >= 0; i--) { + if (!mRunningSwAnimators.get(i).isRunning()) { + mRunningSwAnimators.remove(i); + } + } + } + } + + private void drawHardware(DisplayListCanvas c, Paint p) { + startPending(c); + pruneHwFinished(); + if (mPropPaint != null) { + mUsingProperties = true; + c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint); + } else { + mUsingProperties = false; + drawSoftware(c, p); + } } /** @@ -162,31 +214,115 @@ class RippleForeground extends RippleComponent { return mHasFinishedExit; } - @Override - protected Animator createSoftwareEnter(boolean fast) { + private long computeFadeOutDelay() { + long timeSinceEnter = AnimationUtils.currentAnimationTimeMillis() - mEnterStartedAtMillis; + if (timeSinceEnter > 0 && timeSinceEnter < OPACITY_HOLD_DURATION) { + return OPACITY_HOLD_DURATION - timeSinceEnter; + } + return 0; + } + + private void startSoftwareEnter() { + for (int i = 0; i < mRunningSwAnimators.size(); i++) { + mRunningSwAnimators.get(i).cancel(); + } + mRunningSwAnimators.clear(); + final int duration = getRadiusDuration(); final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1); - tweenRadius.setAutoCancel(true); tweenRadius.setDuration(duration); tweenRadius.setInterpolator(DECELERATE_INTERPOLATOR); - tweenRadius.setStartDelay(RIPPLE_ENTER_DELAY); + tweenRadius.start(); + mRunningSwAnimators.add(tweenRadius); final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1); - tweenOrigin.setAutoCancel(true); tweenOrigin.setDuration(duration); tweenOrigin.setInterpolator(DECELERATE_INTERPOLATOR); - tweenOrigin.setStartDelay(RIPPLE_ENTER_DELAY); + tweenOrigin.start(); + mRunningSwAnimators.add(tweenOrigin); final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1); - opacity.setAutoCancel(true); - opacity.setDuration(OPACITY_ENTER_DURATION_FAST); + opacity.setDuration(OPACITY_ENTER_DURATION); opacity.setInterpolator(LINEAR_INTERPOLATOR); + opacity.start(); + mRunningSwAnimators.add(opacity); + } - final AnimatorSet set = new AnimatorSet(); - set.play(tweenOrigin).with(tweenRadius).with(opacity); + private void startSoftwareExit() { + final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 0); + opacity.setDuration(OPACITY_EXIT_DURATION); + opacity.setInterpolator(LINEAR_INTERPOLATOR); + opacity.addListener(mAnimationListener); + opacity.setStartDelay(computeFadeOutDelay()); + opacity.start(); + mRunningSwAnimators.add(opacity); + } - return set; + private void startHardwareEnter() { + if (mForceSoftware) { return; } + mPropX = CanvasProperty.createFloat(getCurrentX()); + mPropY = CanvasProperty.createFloat(getCurrentY()); + mPropRadius = CanvasProperty.createFloat(getCurrentRadius()); + final Paint paint = mOwner.getRipplePaint(); + mPropPaint = CanvasProperty.createPaint(paint); + + final int radiusDuration = getRadiusDuration(); + + final RenderNodeAnimator radius = new RenderNodeAnimator(mPropRadius, mTargetRadius); + radius.setDuration(radiusDuration); + radius.setInterpolator(DECELERATE_INTERPOLATOR); + mPendingHwAnimators.add(radius); + + final RenderNodeAnimator x = new RenderNodeAnimator(mPropX, mTargetX); + x.setDuration(radiusDuration); + x.setInterpolator(DECELERATE_INTERPOLATOR); + mPendingHwAnimators.add(x); + + final RenderNodeAnimator y = new RenderNodeAnimator(mPropY, mTargetY); + y.setDuration(radiusDuration); + y.setInterpolator(DECELERATE_INTERPOLATOR); + mPendingHwAnimators.add(y); + + final RenderNodeAnimator opacity = new RenderNodeAnimator(mPropPaint, + RenderNodeAnimator.PAINT_ALPHA, paint.getAlpha()); + opacity.setDuration(OPACITY_ENTER_DURATION); + opacity.setInterpolator(LINEAR_INTERPOLATOR); + opacity.setStartValue(0); + mPendingHwAnimators.add(opacity); + + invalidateSelf(); + } + + private void startHardwareExit() { + // Only run a hardware exit if we had a hardware enter to continue from + if (mForceSoftware || mPropPaint == null) return; + + final RenderNodeAnimator opacity = new RenderNodeAnimator(mPropPaint, + RenderNodeAnimator.PAINT_ALPHA, 0); + opacity.setDuration(OPACITY_EXIT_DURATION); + opacity.setInterpolator(LINEAR_INTERPOLATOR); + opacity.addListener(mAnimationListener); + opacity.setStartDelay(computeFadeOutDelay()); + mPendingHwAnimators.add(opacity); + invalidateSelf(); + } + + /** + * Starts a ripple enter animation. + */ + public final void enter() { + mEnterStartedAtMillis = AnimationUtils.currentAnimationTimeMillis(); + startSoftwareEnter(); + startHardwareEnter(); + } + + /** + * Starts a ripple exit animation. + */ + public final void exit() { + startSoftwareExit(); + startHardwareExit(); } private float getCurrentX() { @@ -207,96 +343,23 @@ class RippleForeground extends RippleComponent { return MathUtils.lerp(mStartRadius, mTargetRadius, mTweenRadius); } - private int getOpacityExitDuration() { - return (int) (1000 * mOpacity / WAVE_OPACITY_DECAY_VELOCITY + 0.5f); - } + /** + * Draws the ripple to the canvas, inheriting the paint's color and alpha + * properties. + * + * @param c the canvas to which the ripple should be drawn + * @param p the paint used to draw the ripple + */ + public void draw(Canvas c, Paint p) { + final boolean hasDisplayListCanvas = !mForceSoftware && c instanceof DisplayListCanvas; - @Override - protected Animator createSoftwareExit() { - final int radiusDuration; - final int originDuration; - final int opacityDuration; - - radiusDuration = getRadiusDuration(); - originDuration = radiusDuration; - opacityDuration = getOpacityExitDuration(); - - final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1); - tweenRadius.setAutoCancel(true); - tweenRadius.setDuration(radiusDuration); - tweenRadius.setInterpolator(DECELERATE_INTERPOLATOR); - - final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1); - tweenOrigin.setAutoCancel(true); - tweenOrigin.setDuration(originDuration); - tweenOrigin.setInterpolator(DECELERATE_INTERPOLATOR); - - final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 0); - opacity.setAutoCancel(true); - opacity.setDuration(opacityDuration); - opacity.setInterpolator(LINEAR_INTERPOLATOR); - - final AnimatorSet set = new AnimatorSet(); - set.play(tweenOrigin).with(tweenRadius).with(opacity); - set.addListener(mAnimationListener); - - return set; - } - - @Override - protected RenderNodeAnimatorSet createHardwareExit(Paint p) { - final int radiusDuration; - final int originDuration; - final int opacityDuration; - - radiusDuration = getRadiusDuration(); - originDuration = radiusDuration; - opacityDuration = getOpacityExitDuration(); - - final float startX = getCurrentX(); - final float startY = getCurrentY(); - final float startRadius = getCurrentRadius(); - - p.setAlpha((int) (p.getAlpha() * mOpacity + 0.5f)); - - mPropPaint = CanvasProperty.createPaint(p); - mPropRadius = CanvasProperty.createFloat(startRadius); - mPropX = CanvasProperty.createFloat(startX); - mPropY = CanvasProperty.createFloat(startY); - - final RenderNodeAnimator radius = new RenderNodeAnimator(mPropRadius, mTargetRadius); - radius.setDuration(radiusDuration); - radius.setInterpolator(DECELERATE_INTERPOLATOR); - - final RenderNodeAnimator x = new RenderNodeAnimator(mPropX, mTargetX); - x.setDuration(originDuration); - x.setInterpolator(DECELERATE_INTERPOLATOR); - - final RenderNodeAnimator y = new RenderNodeAnimator(mPropY, mTargetY); - y.setDuration(originDuration); - y.setInterpolator(DECELERATE_INTERPOLATOR); - - final RenderNodeAnimator opacity = new RenderNodeAnimator(mPropPaint, - RenderNodeAnimator.PAINT_ALPHA, 0); - opacity.setDuration(opacityDuration); - opacity.setInterpolator(LINEAR_INTERPOLATOR); - opacity.addListener(mAnimationListener); - - final RenderNodeAnimatorSet set = new RenderNodeAnimatorSet(); - set.add(radius); - set.add(opacity); - set.add(x); - set.add(y); - - return set; - } - - @Override - protected void jumpValuesToExit() { - mOpacity = 0; - mTweenX = 1; - mTweenY = 1; - mTweenRadius = 1; + pruneSwFinished(); + if (hasDisplayListCanvas) { + final DisplayListCanvas hw = (DisplayListCanvas) c; + drawHardware(hw, p); + } else { + drawSoftware(c, p); + } } /** @@ -319,10 +382,39 @@ class RippleForeground extends RippleComponent { } } + /** + * Ends all animations, jumping values to the end state. + */ + public void end() { + for (int i = 0; i < mRunningSwAnimators.size(); i++) { + mRunningSwAnimators.get(i).end(); + } + mRunningSwAnimators.clear(); + for (int i = 0; i < mRunningHwAnimators.size(); i++) { + mRunningHwAnimators.get(i).end(); + } + mRunningHwAnimators.clear(); + } + + private void onAnimationPropertyChanged() { + if (!mUsingProperties) { + invalidateSelf(); + } + } + private final AnimatorListenerAdapter mAnimationListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animator) { mHasFinishedExit = true; + pruneHwFinished(); + pruneSwFinished(); + + if (mRunningHwAnimators.isEmpty()) { + mPropPaint = null; + mPropRadius = null; + mPropX = null; + mPropY = null; + } } }; @@ -361,7 +453,7 @@ class RippleForeground extends RippleComponent { @Override public void setValue(RippleForeground object, float value) { object.mTweenRadius = value; - object.invalidateSelf(); + object.onAnimationPropertyChanged(); } @Override @@ -375,18 +467,18 @@ class RippleForeground extends RippleComponent { */ private static final FloatProperty TWEEN_ORIGIN = new FloatProperty("tweenOrigin") { - @Override - public void setValue(RippleForeground object, float value) { - object.mTweenX = value; - object.mTweenY = value; - object.invalidateSelf(); - } + @Override + public void setValue(RippleForeground object, float value) { + object.mTweenX = value; + object.mTweenY = value; + object.onAnimationPropertyChanged(); + } - @Override - public Float get(RippleForeground object) { - return object.mTweenX; - } - }; + @Override + public Float get(RippleForeground object) { + return object.mTweenX; + } + }; /** * Property for animating opacity between 0 and its target value. @@ -396,7 +488,7 @@ class RippleForeground extends RippleComponent { @Override public void setValue(RippleForeground object, float value) { object.mOpacity = value; - object.invalidateSelf(); + object.onAnimationPropertyChanged(); } @Override From 6b1f5732eb55160163cba89b36243465f02a3942 Mon Sep 17 00:00:00 2001 From: chaviw Date: Thu, 2 Nov 2017 11:27:51 -0700 Subject: [PATCH 019/226] Fix window cropping issue. Comparing mLayer to mSystemDecorLayer no longer makes sense since mLayer is always zero. The code was always falling into this check and causing the crop to be calculated incorrectly by ignoring system decor. Test: Apps in split screen now have the background when draging. Change-Id: I3e310390fcc840d9ae869ed58243a9f8ff5d1337 (cherry picked from commit 0eac168982c393c71d1fef7711733f39c07b0290) --- services/core/java/com/android/server/wm/WindowState.java | 3 --- .../src/com/android/server/wm/WindowFrameTests.java | 8 +++----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 502fc127e0cf9..a2323a46d234c 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -4111,9 +4111,6 @@ class WindowState extends WindowContainer implements WindowManagerP policyCrop.intersect(-mCompatFrame.left, -mCompatFrame.top, displayInfo.logicalWidth - mCompatFrame.left, displayInfo.logicalHeight - mCompatFrame.top); - } else if (mLayer >= mService.mSystemDecorLayer) { - // Above the decor layer is easy, just use the entire window - policyCrop.set(0, 0, mCompatFrame.width(), mCompatFrame.height()); } else if (mDecorFrame.isEmpty()) { // Windows without policy decor aren't cropped. policyCrop.set(0, 0, mCompatFrame.width(), mCompatFrame.height()); diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java index 503e1ac4563dd..fdcf57b182415 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java @@ -23,6 +23,7 @@ import org.junit.runner.RunWith; import android.app.ActivityManager.TaskDescription; import android.content.res.Configuration; import android.graphics.Rect; +import android.os.Debug; import android.platform.test.annotations.Presubmit; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; @@ -339,11 +340,8 @@ public class WindowFrameTests extends WindowTestsBase { w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null); w.calculatePolicyCrop(policyCrop); - // If we were above system decor we wouldnt' get any cropping though - w.mLayer = sWm.mSystemDecorLayer + 1; - w.calculatePolicyCrop(policyCrop); - assertRect(policyCrop, 0, 0, logicalWidth, logicalHeight); - w.mLayer = 1; + assertRect(policyCrop, 0, cf.top, logicalWidth, cf.bottom); + dcf.setEmpty(); // Likewise with no decor frame we would get no crop w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null); From 79533f45665f450321c1c894ec4cbe14451a1457 Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Wed, 22 Nov 2017 12:47:58 -0800 Subject: [PATCH 020/226] Correct ordering of status bar panel layers. Not sure what bug was hiding this before but seems pretty clear, that this is the ordering we need. See the documentation, APPLICATION_PANEL, etc... Bug: 69591927 Test: Manual Change-Id: I82fc011aff6122efa5c3ed63da154761e7065612 (cherry picked from commit 8360a782fa529b142673b921f1d178f4b3d0be93) --- core/java/android/view/WindowManager.java | 6 ++++-- .../server/policy/WindowManagerPolicy.java | 6 +++--- .../com/android/server/wm/ZOrderingTests.java | 20 +++++++++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index eb5fc92e5ccc2..905c0715ecb89 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -604,8 +604,10 @@ public interface WindowManager extends ViewManager { public static final int TYPE_DRAG = FIRST_SYSTEM_WINDOW+16; /** - * Window type: panel that slides out from under the status bar - * In multiuser systems shows on all users' windows. + * Window type: panel that slides out from over the status bar + * In multiuser systems shows on all users' windows. These windows + * are displayed on top of the stauts bar and any {@link #TYPE_STATUS_BAR_PANEL} + * windows. * @hide */ public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17; diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 8d56eb80181ae..9e97d846b5217 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -808,11 +808,11 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { case TYPE_INPUT_METHOD_DIALOG: // on-screen keyboards and other such input method user interfaces go here. return 15; - case TYPE_STATUS_BAR_SUB_PANEL: - return 17; case TYPE_STATUS_BAR: - return 18; + return 17; case TYPE_STATUS_BAR_PANEL: + return 18; + case TYPE_STATUS_BAR_SUB_PANEL: return 19; case TYPE_KEYGUARD_DIALOG: return 20; diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java index 4e23b515eb4ba..04f5e5e919aa0 100644 --- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java @@ -42,6 +42,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; /** * Tests for the {@link WindowLayersController} class. @@ -323,4 +326,21 @@ public class ZOrderingTests extends WindowTestsBase { assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow); assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow); } + + @Test + public void testAssignWindowLayers_ForSysUiPanels() throws Exception { + final WindowState navBarPanel = + createWindow(null, TYPE_NAVIGATION_BAR_PANEL, mDisplayContent, "NavBarPanel"); + final WindowState statusBarPanel = + createWindow(null, TYPE_STATUS_BAR_PANEL, mDisplayContent, "StatusBarPanel"); + final WindowState statusBarSubPanel = + createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, mDisplayContent, "StatusBarSubPanel"); + mDisplayContent.assignChildLayers(mTransaction); + + // Ime should be above all app windows and below system windows if it is targeting an app + // window. + assertWindowLayerGreaterThan(mTransaction, navBarPanel, mNavBarWindow); + assertWindowLayerGreaterThan(mTransaction, statusBarPanel, mStatusBarWindow); + assertWindowLayerGreaterThan(mTransaction, statusBarSubPanel, statusBarPanel); + } } From ee33dce1323677ad4fff56b79b62f9a981e468c4 Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Thu, 30 Nov 2017 13:09:22 -0500 Subject: [PATCH 021/226] Propagate scrim alpha to LightBarController Change-Id: I8f89c0ccb5811ce729f04041dc1ff8efa3877991 Fixes: 69895859 Test: Launch settings, chrome, go back to launcher (cherry picked from commit 6afae3702f12f9e42d4b5c3f4552cc19775007d1) --- .../android/systemui/statusbar/phone/ScrimController.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index dfd4c17682684..3a36776304f2a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -629,6 +629,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } } + // TODO factor mLightBarController out of this class + if (scrim == mScrimBehind) { + mLightBarController.setScrimAlpha(alpha); + } + final ScrimView scrimView = scrim instanceof ScrimView ? (ScrimView) scrim : null; final boolean wantsAlphaUpdate = alpha != currentAlpha && alpha != animEndValue; final boolean wantsTintUpdate = scrimView != null From ae5f0f0861889eda3b4310049b9c4e5388265490 Mon Sep 17 00:00:00 2001 From: chaviw Date: Mon, 4 Dec 2017 18:39:34 -0800 Subject: [PATCH 022/226] Ensure non app WindowTokens are removed when closing. Non app WindowTokens weren't propery removed when closing. This would cause several layers to stay around in the system. Bug: 69852584 Test: Turn phone screen off and back on again. Window is removed. Change-Id: I51674ebdab129b462e958c3027a26ee6feeffb9f (cherry picked from commit aec55ff58ca4ae1056744015cc72771866fc347c) --- services/core/java/com/android/server/wm/WindowToken.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index c9d7b70d69510..a3d4b71a68afd 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -125,6 +125,11 @@ class WindowToken extends WindowContainer { } void setExiting() { + if (mChildren.size() == 0) { + super.removeImmediately(); + return; + } + // This token is exiting, so allow it to be removed when it no longer contains any windows. mPersistOnEmpty = false; From 7f3e53245c0c731cf9849c368f7aa038484ed3a8 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Tue, 5 Dec 2017 10:20:39 -0500 Subject: [PATCH 023/226] Undo changes accidentally committed with another cl Test: runtest systemui Change-Id: I4019d0063bc29f4f83433d6b4701cec20747dc4b (cherry picked from commit 86cd551fc616e1da4b87330d73a0bb687b6dae29) --- .../SystemUI/res/layout/volume_dialog.xml | 4 +- .../systemui/volume/VolumeDialogImpl.java | 62 ++++--------------- 2 files changed, 13 insertions(+), 53 deletions(-) diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index 863f17b432f2b..fac254a0083a0 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - - + diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index f16d7b8d3745e..0d41e2029086c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -19,9 +19,6 @@ package com.android.systemui.volume; import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK; import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC; -import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE; -import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE; -import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE; import static com.android.systemui.volume.Events.DISMISS_REASON_TOUCH_OUTSIDE; import android.accessibilityservice.AccessibilityServiceInfo; @@ -45,7 +42,6 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; -import android.provider.Settings; import android.provider.Settings.Global; import android.util.Log; import android.util.Slog; @@ -71,7 +67,6 @@ import android.widget.TextView; import com.android.settingslib.Utils; import com.android.systemui.Dependency; -import com.android.systemui.HardwareBgDrawable; import com.android.systemui.HardwareUiLayout; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -79,7 +74,6 @@ import com.android.systemui.plugins.VolumeDialog; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.plugins.VolumeDialogController.State; import com.android.systemui.plugins.VolumeDialogController.StreamState; -import com.android.systemui.util.leak.RotationUtils; import java.io.PrintWriter; import java.util.ArrayList; @@ -103,13 +97,9 @@ public class VolumeDialogImpl implements VolumeDialog { private final VolumeDialogController mController; private Window mWindow; - //private HardwareUiLayout mHardwareLayout; + private HardwareUiLayout mHardwareLayout; private CustomDialog mDialog; private ViewGroup mDialogView; - private boolean mEdgeBleed; - private boolean mRoundedDivider; - private HardwareBgDrawable mBackground; - private int mRotation = ROTATION_NONE; private ViewGroup mDialogRowsView; private ViewGroup mDialogContentView; private final List mRows = new ArrayList<>(); @@ -121,8 +111,6 @@ public class VolumeDialogImpl implements VolumeDialog { private final Accessibility mAccessibility = new Accessibility(); private final ColorStateList mActiveSliderTint; private final ColorStateList mInactiveSliderTint; - private static final String EDGE_BLEED = "sysui_hwui_edge_bleed"; - private static final String ROUNDED_DIVIDER = "sysui_hwui_rounded_divider"; private boolean mShowing; private boolean mShowA11yStream; @@ -193,16 +181,8 @@ public class VolumeDialogImpl implements VolumeDialog { return true; } }); - - mEdgeBleed = Settings.Secure.getInt(mContext.getContentResolver(), - EDGE_BLEED, 0) != 0; - mRoundedDivider = Settings.Secure.getInt(mContext.getContentResolver(), - ROUNDED_DIVIDER, 1) != 0; - updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding()); - mBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed, mContext); - mDialogView.setBackground(mBackground); - //mHardwareLayout = HardwareUiLayout.get(mDialogView); - //mHardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE)); + mHardwareLayout = HardwareUiLayout.get(mDialogView); + mHardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE)); mDialogContentView = mDialog.findViewById(R.id.volume_dialog_content); mDialogRowsView = mDialogContentView.findViewById(R.id.volume_dialog_rows); @@ -230,25 +210,6 @@ public class VolumeDialogImpl implements VolumeDialog { updateRowsH(getActiveRow()); } - private int getEdgePadding() { - return mContext.getResources().getDimensionPixelSize(R.dimen.edge_margin); - } - - private void updateEdgeMargin(int edge) { - if (mDialogView != null) { - mRotation = RotationUtils.getRotation(mContext); - ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mDialogView.getLayoutParams(); - if (mRotation == ROTATION_LANDSCAPE) { - params.topMargin = edge; - } else if (mRotation == ROTATION_SEASCAPE) { - params.bottomMargin = edge; - } else { - params.rightMargin = edge; - } - mDialogView.setLayoutParams(params); - } - } - private ColorStateList loadColorStateList(int colorResId) { return ColorStateList.valueOf(mContext.getColor(colorResId)); } @@ -428,11 +389,11 @@ public class VolumeDialogImpl implements VolumeDialog { rescheduleTimeoutH(); if (mShowing) return; mShowing = true; - mDialogView.setTranslationY(getAnimTranslation()); - mDialogView.setAlpha(0); - mDialogView.animate() + mHardwareLayout.setTranslationX(getAnimTranslation()); + mHardwareLayout.setAlpha(0); + mHardwareLayout.animate() .alpha(1) - .translationY(0) + .translationX(0) .setDuration(300) .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .withEndAction(() -> { @@ -471,17 +432,16 @@ public class VolumeDialogImpl implements VolumeDialog { mHandler.removeMessages(H.SHOW); if (!mShowing) return; mShowing = false; - mDialogView.setTranslationX(0); - mDialogView.setAlpha(1); - mDialogView.animate() + mHardwareLayout.setTranslationX(0); + mHardwareLayout.setAlpha(1); + mHardwareLayout.animate() .alpha(0) .translationX(getAnimTranslation()) .setDuration(300) .withEndAction(() -> mDialog.dismiss()) .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator()) .start(); - if (mAccessibilityMgr.isObservedEventType( - AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)) { + if (mAccessibilityMgr.isEnabled()) { AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); event.setPackageName(mContext.getPackageName()); From f2ef93109375e767ec81ec6e5aac7aca08ff4126 Mon Sep 17 00:00:00 2001 From: John Reck Date: Wed, 6 Dec 2017 09:46:13 -0800 Subject: [PATCH 024/226] targetSdkVersion float property sanitization Bug: 70242902 Test: CTS tests checking for exception still pass Change-Id: I77dd772a6cb7077de058a3ca85b984c2c3f7321e (cherry picked from commit c888788cb969a5cd3a17ab396a2e02555d2717f5) --- core/java/android/view/View.java | 57 +++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0525ab164b028..1e75fc9670755 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -893,6 +893,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static boolean sAutoFocusableOffUIThreadWontNotifyParents; + /** + * Prior to P things like setScaleX() allowed passing float values that were bogus such as + * Float.NaN. If the app is targetting P or later then passing these values will result in an + * exception being thrown. If the app is targetting an earlier SDK version, then we will + * silently clamp these values to avoid crashes elsewhere when the rendering code hits + * these bogus values. + */ + private static boolean sThrowOnInvalidFloatProperties; + /** @hide */ @IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO}) @Retention(RetentionPolicy.SOURCE) @@ -4752,6 +4761,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, sUseDefaultFocusHighlight = context.getResources().getBoolean( com.android.internal.R.bool.config_useDefaultFocusHighlight); + sThrowOnInvalidFloatProperties = targetSdkVersion >= Build.VERSION_CODES.P; + sCompatibilityDone = true; } } @@ -14275,7 +14286,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public void setScaleX(float scaleX) { if (scaleX != getScaleX()) { - requireIsFinite(scaleX, "scaleX"); + scaleX = sanitizeFloatPropertyValue(scaleX, "scaleX"); invalidateViewProperty(true, false); mRenderNode.setScaleX(scaleX); invalidateViewProperty(false, true); @@ -14312,7 +14323,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public void setScaleY(float scaleY) { if (scaleY != getScaleY()) { - requireIsFinite(scaleY, "scaleY"); + scaleY = sanitizeFloatPropertyValue(scaleY, "scaleY"); invalidateViewProperty(true, false); mRenderNode.setScaleY(scaleY); invalidateViewProperty(false, true); @@ -14862,13 +14873,41 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - private static void requireIsFinite(float transform, String propertyName) { - if (Float.isNaN(transform)) { - throw new IllegalArgumentException("Cannot set '" + propertyName + "' to Float.NaN"); + private static float sanitizeFloatPropertyValue(float value, String propertyName) { + return sanitizeFloatPropertyValue(value, propertyName, -Float.MAX_VALUE, Float.MAX_VALUE); + } + + private static float sanitizeFloatPropertyValue(float value, String propertyName, + float min, float max) { + // The expected "nothing bad happened" path + if (value >= min && value <= max) return value; + + if (value < min || value == Float.NEGATIVE_INFINITY) { + if (sThrowOnInvalidFloatProperties) { + throw new IllegalArgumentException("Cannot set '" + propertyName + "' to " + + value + ", the value must be >= " + min); + } + return min; } - if (Float.isInfinite(transform)) { - throw new IllegalArgumentException("Cannot set '" + propertyName + "' to infinity"); + + if (value > max || value == Float.POSITIVE_INFINITY) { + if (sThrowOnInvalidFloatProperties) { + throw new IllegalArgumentException("Cannot set '" + propertyName + "' to " + + value + ", the value must be <= " + max); + } + return max; } + + if (Float.isNaN(value)) { + if (sThrowOnInvalidFloatProperties) { + throw new IllegalArgumentException( + "Cannot set '" + propertyName + "' to Float.NaN"); + } + return 0; // Unclear which direction this NaN went so... 0? + } + + // Shouldn't be possible to reach this. + throw new IllegalStateException("How do you get here?? " + value); } /** @@ -14957,7 +14996,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public void setElevation(float elevation) { if (elevation != getElevation()) { - requireIsFinite(elevation, "elevation"); + elevation = sanitizeFloatPropertyValue(elevation, "elevation"); invalidateViewProperty(true, false); mRenderNode.setElevation(elevation); invalidateViewProperty(false, true); @@ -15050,7 +15089,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public void setTranslationZ(float translationZ) { if (translationZ != getTranslationZ()) { - requireIsFinite(translationZ, "translationZ"); + translationZ = sanitizeFloatPropertyValue(translationZ, "translationZ"); invalidateViewProperty(true, false); mRenderNode.setTranslationZ(translationZ); invalidateViewProperty(false, true); From 393cd3530b85146c146fc8dff73a66b8de0bbac5 Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Wed, 6 Dec 2017 13:59:25 -0800 Subject: [PATCH 025/226] Fix issue where scrims would not go away Change-Id: Ibb683d6bddadd5b670da28427d4801e03d575bd8 Fixes: 70245015 Test: runtest -x packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java (cherry picked from commit c2e754692152da37b4fc5db82241bf4f59f276b5) --- .../systemui/statusbar/phone/StatusBar.java | 1 + .../systemui/statusbar/phone/StatusBarTest.java | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 7c334375d47e6..3e32446296309 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -5112,6 +5112,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void notifyFpAuthModeChanged() { updateDozing(); + updateScrimController(); } private void updateDozing() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 713f7843eaea8..0aeb7b6bf34cb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -104,6 +104,7 @@ public class StatusBarTest extends SysuiTestCase { PowerManager mPowerManager; SystemServicesProxy mSystemServicesProxy; NotificationPanelView mNotificationPanelView; + ScrimController mScrimController; IStatusBarService mBarService; ArrayList mNotificationList; private DisplayMetrics mDisplayMetrics = new DisplayMetrics(); @@ -131,6 +132,7 @@ public class StatusBarTest extends SysuiTestCase { mNotificationPanelView = mock(NotificationPanelView.class); when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0)); mNotificationList = mock(ArrayList.class); + mScrimController = mock(ScrimController.class); IPowerManager powerManagerService = mock(IPowerManager.class); HandlerThread handlerThread = new HandlerThread("TestThread"); handlerThread.start(); @@ -143,7 +145,7 @@ public class StatusBarTest extends SysuiTestCase { mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache, mKeyguardIndicationController, mStackScroller, mHeadsUpManager, mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView, - mBarService); + mBarService, mScrimController); mStatusBar.mContext = mContext; mStatusBar.mComponents = mContext.getComponents(); doAnswer(invocation -> { @@ -532,12 +534,21 @@ public class StatusBarTest extends SysuiTestCase { mStatusBar.updateKeyguardState(false, false); } + @Test + public void testFingerprintNotification_UpdatesScrims() { + mStatusBar.mStatusBarWindowManager = mock(StatusBarWindowManager.class); + mStatusBar.mFingerprintUnlockController = mock(FingerprintUnlockController.class); + mStatusBar.mDozeScrimController = mock(DozeScrimController.class); + mStatusBar.notifyFpAuthModeChanged(); + verify(mScrimController).transitionTo(any(), any()); + } + static class TestableStatusBar extends StatusBar { public TestableStatusBar(StatusBarKeyguardViewManager man, UnlockMethodCache unlock, KeyguardIndicationController key, NotificationStackScrollLayout stack, HeadsUpManager hum, NotificationData nd, PowerManager pm, SystemServicesProxy ssp, NotificationPanelView panelView, - IStatusBarService barService) { + IStatusBarService barService, ScrimController scrimController) { mStatusBarKeyguardViewManager = man; mUnlockMethodCache = unlock; mKeyguardIndicationController = key; @@ -550,7 +561,7 @@ public class StatusBarTest extends SysuiTestCase { mNotificationPanel = panelView; mBarService = barService; mWakefulnessLifecycle = createAwakeWakefulnessLifecycle(); - mScrimController = mock(ScrimController.class); + mScrimController = scrimController; } private WakefulnessLifecycle createAwakeWakefulnessLifecycle() { From 130474736082cab31c493a8a9a14af2f839db39f Mon Sep 17 00:00:00 2001 From: Bo Hu Date: Wed, 6 Dec 2017 22:17:32 +0000 Subject: [PATCH 026/226] Revert "Add odm sepolicy support to SELinuxMMAC.java" This reverts commit fffa6d238a31ac5e7b30a6e1d852c8c2cc22af1d. Reason for revert: broke mac build b/70273082 FAILED: out/target/product/generic_x86/obj/ETC/vendor_sepolicy.cil_intermediates/vendor_sepolicy.cil /bin/bash -c "(out/host/darwin-x86/bin/version_policy -b out/target/product/generic_x86/obj/FAKE/selinux_policy_intermediates/plat_pub_policy.cil -t out/target/product/generic_x86/obj/ETC/vendor_sepolicy.cil_intermediates/vendor_policy_raw.cil -n 10000.0 -o out/target/product/generic_x86/obj/ETC/vendor_sepolicy.cil_intermediates/vendor_sepolicy.cil.tmp ) && (grep -Fxv -f out/target/product/generic_x86/obj/ETC/plat_pub_versioned.cil_intermediates/plat_pub_versioned.cil out/target/product/generic_x86/obj/ETC/vendor_sepolicy.cil_intermediates/vendor_sepolicy.cil.tmp > out/target/product/generic_x86/obj/ETC/vendor_sepolicy.cil_intermediates/vendor_sepolicy.cil ) && (out/host/darwin-x86/bin/secilc -m -M true -G -N -c 30 out/target/product/generic_x86/obj/ETC/plat_sepolicy.cil_intermediates/plat_sepolicy.cil out/target/product/generic_x86/obj/ETC/plat_pub_versioned.cil_intermediates/plat_pub_versioned.cil out/target/product/generic_x86/obj/ETC/10000.0.cil_intermediates/10000.0.cil out/target/product/generic_x86/obj/ETC/vendor_sepolicy.cil_intermediates/vendor_sepolicy.cil -o /dev/null -f /dev/null )" Parsing out/target/product/generic_x86/obj/FAKE/selinux_policy_intermediates/plat_pub_policy.cil Parsing out/target/product/generic_x86/obj/ETC/vendor_sepolicy.cil_intermediates/vendor_policy_raw.cil grep: out of memory Change-Id: I186f7bc68a76d7b2d717875791ee2fe1828c3598 (cherry picked from commit 11f214d8f3b52dccb231990b5ac04c1e0f05617b) --- .../com/android/server/pm/SELinuxMMAC.java | 56 +++++-------------- 1 file changed, 13 insertions(+), 43 deletions(-) diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java index 388491642e1ef..f0ce3c9d230e8 100644 --- a/services/core/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java @@ -60,8 +60,10 @@ public final class SELinuxMMAC { // to synchronize access during policy load and access attempts. private static List sPolicies = new ArrayList<>(); - // Required MAC permissions files. - private static List sMacPermissions = new ArrayList<>(); + /** Path to MAC permissions on system image */ + private static final File[] MAC_PERMISSIONS = + { new File(Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml"), + new File(Environment.getVendorDirectory(), "/etc/selinux/nonplat_mac_permissions.xml") }; // Append privapp to existing seinfo label private static final String PRIVILEGED_APP_STR = ":privapp"; @@ -74,11 +76,11 @@ public final class SELinuxMMAC { /** * Load the mac_permissions.xml file containing all seinfo assignments used to - * label apps. The loaded mac_permissions.xml files are plat_mac_permissions.xml and - * vendor_mac_permissions.xml, on /system and /vendor partitions, respectively. - * odm_mac_permissions.xml on /odm partition is optional. For further guidance on + * label apps. The loaded mac_permissions.xml file is determined by the + * MAC_PERMISSIONS class variable which is set at class load time which itself + * is based on the USE_OVERRIDE_POLICY class variable. For further guidance on * the proper structure of a mac_permissions.xml file consult the source code - * located at system/sepolicy/private/mac_permissions.xml. + * located at system/sepolicy/mac_permissions.xml. * * @return boolean indicating if policy was correctly loaded. A value of false * typically indicates a structural problem with the xml or incorrectly @@ -91,42 +93,10 @@ public final class SELinuxMMAC { FileReader policyFile = null; XmlPullParser parser = Xml.newPullParser(); - - synchronized (sMacPermissions) { - // Only initialize it once. - if (sMacPermissions.isEmpty()) { - // Platform mac permissions. - sMacPermissions.add(new File( - Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml")); - - // Vendor mac permissions. - // The filename has been renamed from nonplat_mac_permissions to - // vendor_mac_permissions. Either of them should exist. - File vendorMacPermission = new File( - Environment.getVendorDirectory(), "/etc/selinux/vendor_mac_permissions.xml"); - if (vendorMacPermission.exists()) { - sMacPermissions.add(vendorMacPermission); - } else { - // For backward compatibility. - sMacPermissions.add(new File(Environment.getVendorDirectory(), - "/etc/selinux/nonplat_mac_permissions.xml")); - } - - // ODM mac permissions (optional). - File odmMacPermission = new File( - Environment.getOdmDirectory(), "/etc/selinux/odm_mac_permissions.xml"); - if (odmMacPermission.exists()) { - sMacPermissions.add(odmMacPermission); - } - } - } - - final int count = sMacPermissions.size(); - for (int i = 0; i < count; ++i) { - File macPermission = sMacPermissions.get(i); + for (int i = 0; i < MAC_PERMISSIONS.length; i++) { try { - policyFile = new FileReader(macPermission); - Slog.d(TAG, "Using policy file " + macPermission); + policyFile = new FileReader(MAC_PERMISSIONS[i]); + Slog.d(TAG, "Using policy file " + MAC_PERMISSIONS[i]); parser.setInput(policyFile); parser.nextTag(); @@ -150,13 +120,13 @@ public final class SELinuxMMAC { StringBuilder sb = new StringBuilder("Exception @"); sb.append(parser.getPositionDescription()); sb.append(" while parsing "); - sb.append(macPermission); + sb.append(MAC_PERMISSIONS[i]); sb.append(":"); sb.append(ex); Slog.w(TAG, sb.toString()); return false; } catch (IOException ioe) { - Slog.w(TAG, "Exception parsing " + macPermission, ioe); + Slog.w(TAG, "Exception parsing " + MAC_PERMISSIONS[i], ioe); return false; } finally { IoUtils.closeQuietly(policyFile); From a5315293afab6df9e9c7d00c2efb692a48db1bdb Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Thu, 7 Dec 2017 09:46:49 -0800 Subject: [PATCH 027/226] Fix cyclic keyguard state notification We should not not notify that the keyguard faded away when a transition is cancelled. Another transition is already happening and the message will be receive when it finishes. Change-Id: I4d2e227027a02f2168578bc1d201a4cf8672097d Fixes: 70316977 Test: Double tap power button on the lock screen and aod Test: Unlock with fingerprint from aod and lock screen Test: Unlock with PIN Test: Unlock from "pulsing" (AoD2) with fp or by tapping notification (cherry picked from commit 243c731f7f70bd1fa1fe995c52d065ae2d63279d) --- .../systemui/statusbar/phone/StatusBar.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 3e32446296309..59533e602ae16 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -649,15 +649,6 @@ public class StatusBar extends SystemUI implements DemoMode, .Callback() { @Override public void onFinished() { - notifyKeyguardState(); - } - - @Override - public void onCancelled() { - notifyKeyguardState(); - } - - private void notifyKeyguardState() { if (mStatusBarKeyguardViewManager == null) { Log.w(TAG, "Tried to notify keyguard visibility when " + "mStatusBarKeyguardViewManager was null"); @@ -665,6 +656,12 @@ public class StatusBar extends SystemUI implements DemoMode, } mStatusBarKeyguardViewManager.onKeyguardFadedAway(); } + + @Override + public void onCancelled() { + // Transition was cancelled because another one took over. + // Nothing to do in here but wait. + } }; private NotificationMessagingUtil mMessagingUtil; From 0406c586e89af0a1cad52fb76c5fa70ef2a3a431 Mon Sep 17 00:00:00 2001 From: Jack He Date: Fri, 8 Dec 2017 12:21:37 -0800 Subject: [PATCH 028/226] Bluetooth: Fix boolean logic in quiet mode enable * Change If97c454a8e5aff34c4f8550f7ade3da413a200b7 introduced a bug that flipped the logic of quiet mode enable in the handler for MESSAGE_BLUETOOTH_SERVICE_CONNECTED, causing quiet mode to be enabled when toggling from Settings and system UI. Bug: 70395489 Test: make, toggle bluetooth on/off Change-Id: I405fb462783df0d52d9ce84d2ebe959e56a1aa30 (cherry picked from commit a6e031c9f5a39b40452d29c1ba91b02771960210) --- .../core/java/com/android/server/BluetoothManagerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 763a4e4945937..d9713a517a94c 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -1628,7 +1628,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Do enable request try { - if (mQuietEnable) { + if (!mQuietEnable) { if (!mBluetooth.enable()) { Slog.e(TAG, "IBluetooth.enable() returned false"); } From b66ce759b4d35ef8735d92c7bc056f09469c7e9b Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Fri, 8 Dec 2017 10:36:28 -0800 Subject: [PATCH 029/226] Fix issue with transition cancelling Fixes: 69901116 Fixes: 70296214 Test: Double tap power button to launch camera Test: Unlock with fingerprint, pin, SmartLock Test: Unlock with fingerprint while changing brightness Test: Unlock by tapping notification Change-Id: I487b29c3aa5460cbd120072becb4e0449cffcce6 (cherry picked from commit 8635c4494f75205227b46c48cfe7ba7c75778034) --- .../android/systemui/statusbar/phone/ScrimController.java | 8 ++++---- .../com/android/systemui/statusbar/phone/StatusBar.java | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 3a36776304f2a..05fcbb0a11b6b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -204,12 +204,15 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, throw new IllegalArgumentException("Cannot change to UNINITIALIZED."); } + final ScrimState oldState = mState; + mState = state; + if (mCallback != null) { mCallback.onCancelled(); } mCallback = callback; - state.prepare(mState); + state.prepare(oldState); mScreenBlankingCallbackCalled = false; mAnimationDelay = 0; mBlankScreen = state.getBlanksScreen(); @@ -228,8 +231,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mKeyguardFadeoutAnimation.cancel(); } - mState = state; - // Do not let the device sleep until we're done with all animations if (!mWakeLockHeld) { if (mWakeLock != null) { @@ -310,7 +311,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mCurrentInFrontAlpha = 0; } } else { - Log.w(TAG, "Invalid state, cannot set panel expansion when: " + mState); return; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 59533e602ae16..68c7d849cf0d8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -659,8 +659,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onCancelled() { - // Transition was cancelled because another one took over. - // Nothing to do in here but wait. + onFinished(); } }; From 5ffae1b4fd47e3420e9d00d6d891ca89c9206298 Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Fri, 8 Dec 2017 11:55:19 -0800 Subject: [PATCH 030/226] Fix issue where brightness mirror listener wouldn't be released Change-Id: I4f0bd83ad6f5915145f59e47fb899dfee8cb2d77 Fixes: 70388511 Bug: 70296214 Test: Unlock from pulsing with fingerprint, then adjust brightness (cherry picked from commit 037a50076fd7d64c6d609726e6f81d3c6b335891) --- .../android/systemui/statusbar/phone/StatusBar.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 68c7d849cf0d8..87b786a21461b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -4122,14 +4122,10 @@ public class StatusBar extends SystemUI implements DemoMode, .setStartDelay(0) .setDuration(FADE_KEYGUARD_DURATION_PULSING) .setInterpolator(ScrimController.KEYGUARD_FADE_OUT_INTERPOLATOR) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - hideKeyguard(); - mStatusBarKeyguardViewManager.onKeyguardFadedAway(); - } - }) - .start(); + .withEndAction(()-> { + hideKeyguard(); + mStatusBarKeyguardViewManager.onKeyguardFadedAway(); + }).start(); } /** From b517ae103558f644a1142d8b98ed6c3840bae730 Mon Sep 17 00:00:00 2001 From: Amit Mahajan Date: Mon, 11 Dec 2017 23:30:35 +0000 Subject: [PATCH 031/226] Revert "Add support for send Message with messaging options" This reverts commit bbef627077c5126b5b1292a5a396d784218c09cf. Reason for revert: Causing long sms failure Bug: 68762942 Change-Id: I43c36e3286105459c7e08d1a0b2d89b4c42140b2 (cherry picked from commit b348a347dfc80d45f1b9404f4eea95a5cdc64eba) --- .../java/android/telephony/SmsManager.java | 245 +----------------- .../java/android/telephony/SmsMessage.java | 26 -- .../com/android/internal/telephony/ISms.aidl | 101 -------- .../internal/telephony/cdma/SmsMessage.java | 60 +---- .../internal/telephony/gsm/SmsMessage.java | 113 +------- 5 files changed, 6 insertions(+), 539 deletions(-) diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index fdedf758975a5..7ab75f545bd3a 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -390,112 +390,6 @@ public final class SmsManager { } /** - * Send a text based SMS with messaging options. - * - * @param destinationAddress the address to send the message to - * @param scAddress is the service center address or null to use - * the current default SMSC - * @param text the body of the message to send - * @param sentIntent if not NULL this PendingIntent is - * broadcast when the message is successfully sent, or failed. - * The result code will be Activity.RESULT_OK for success, - * or one of these errors:
- * RESULT_ERROR_GENERIC_FAILURE
- * RESULT_ERROR_RADIO_OFF
- * RESULT_ERROR_NULL_PDU
- * For RESULT_ERROR_GENERIC_FAILURE the sentIntent may include - * the extra "errorCode" containing a radio technology specific value, - * generally only useful for troubleshooting.
- * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applications, - * which cause smaller number of SMS to be sent in checking period. - * @param deliveryIntent if not NULL this PendingIntent is - * broadcast when the message is delivered to the recipient. The - * raw pdu of the status report is in the extended data ("pdu"). - * @param priority Priority level of the message - * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1 - * --------------------------------- - * PRIORITY | Level of Priority - * --------------------------------- - * '00' | Normal - * '01' | Interactive - * '10' | Urgent - * '11' | Emergency - * ---------------------------------- - * Any Other values included Negative considered as Invalid Priority Indicator of the message. - * @param expectMore is a boolean to indicate the sending messages through same link or not. - * @param validityPeriod Validity Period of the message in mins. - * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1. - * Validity Period(Minimum) -> 5 mins - * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks). - * Any Other values included Negative considered as Invalid Validity Period of the message. - * - * @throws IllegalArgumentException if destinationAddress or text are empty - * {@hide} - */ - public void sendTextMessage( - String destinationAddress, String scAddress, String text, - PendingIntent sentIntent, PendingIntent deliveryIntent, - int priority, boolean expectMore, int validityPeriod) { - sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, - true /* persistMessage*/, priority, expectMore, validityPeriod); - } - - private void sendTextMessageInternal( - String destinationAddress, String scAddress, String text, - PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage, - int priority, boolean expectMore, int validityPeriod) { - if (TextUtils.isEmpty(destinationAddress)) { - throw new IllegalArgumentException("Invalid destinationAddress"); - } - - if (TextUtils.isEmpty(text)) { - throw new IllegalArgumentException("Invalid message body"); - } - - if (priority < 0x00 || priority > 0x03) { - throw new IllegalArgumentException("Invalid priority"); - } - - if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) { - throw new IllegalArgumentException("Invalid validity period"); - } - - try { - ISms iccISms = getISmsServiceOrThrow(); - if (iccISms != null) { - iccISms.sendTextForSubscriberWithOptions(getSubscriptionId(), - ActivityThread.currentPackageName(), destinationAddress, scAddress, text, - sentIntent, deliveryIntent, persistMessage, priority, expectMore, - validityPeriod); - } - } catch (RemoteException ex) { - // ignore it - } - } - - /** - * Send a text based SMS without writing it into the SMS Provider. - * - *

Requires Permission: - * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier - * privileges. - *

- * - * @see #sendTextMessage(String, String, String, PendingIntent, - * PendingIntent, int, boolean, int) - * @hide - */ - public void sendTextMessageWithoutPersisting( - String destinationAddress, String scAddress, String text, - PendingIntent sentIntent, PendingIntent deliveryIntent, int priority, - boolean expectMore, int validityPeriod) { - sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, - false /* persistMessage */, priority, expectMore, validityPeriod); - } - - /** - * * Inject an SMS PDU into the android application framework. * *

Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier @@ -653,140 +547,6 @@ public final class SmsManager { } /** - * Send a multi-part text based SMS with messaging options. The callee should have already - * divided the message into correctly sized parts by calling - * divideMessage. - * - *

Note: Using this method requires that your app has the - * {@link android.Manifest.permission#SEND_SMS} permission.

- * - *

Note: Beginning with Android 4.4 (API level 19), if - * and only if an app is not selected as the default SMS app, the system automatically - * writes messages sent using this method to the SMS Provider (the default SMS app is always - * responsible for writing its sent messages to the SMS Provider). For information about - * how to behave as the default SMS app, see {@link android.provider.Telephony}.

- * - * @param destinationAddress the address to send the message to - * @param scAddress is the service center address or null to use - * the current default SMSC - * @param parts an ArrayList of strings that, in order, - * comprise the original message - * @param sentIntents if not null, an ArrayList of - * PendingIntents (one for each message part) that is - * broadcast when the corresponding message part has been sent. - * The result code will be Activity.RESULT_OK for success, - * or one of these errors:
- * RESULT_ERROR_GENERIC_FAILURE
- * RESULT_ERROR_RADIO_OFF
- * RESULT_ERROR_NULL_PDU
- * For RESULT_ERROR_GENERIC_FAILURE each sentIntent may include - * the extra "errorCode" containing a radio technology specific value, - * generally only useful for troubleshooting.
- * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applications, - * which cause smaller number of SMS to be sent in checking period. - * @param deliveryIntents if not null, an ArrayList of - * PendingIntents (one for each message part) that is - * broadcast when the corresponding message part has been delivered - * to the recipient. The raw pdu of the status report is in the - * extended data ("pdu"). - * @param priority Priority level of the message - * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1 - * --------------------------------- - * PRIORITY | Level of Priority - * --------------------------------- - * '00' | Normal - * '01' | Interactive - * '10' | Urgent - * '11' | Emergency - * ---------------------------------- - * Any Other values included Negative considered as Invalid Priority Indicator of the message. - * @param expectMore is a boolean to indicate the sending messages through same link or not. - * @param validityPeriod Validity Period of the message in mins. - * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1. - * Validity Period(Minimum) -> 5 mins - * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks). - * Any Other values included Negative considered as Invalid Validity Period of the message. - * - * @throws IllegalArgumentException if destinationAddress or data are empty - * {@hide} - */ - public void sendMultipartTextMessage( - String destinationAddress, String scAddress, ArrayList parts, - ArrayList sentIntents, ArrayList deliveryIntents, - int priority, boolean expectMore, int validityPeriod) { - sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, - deliveryIntents, true /* persistMessage*/); - } - - private void sendMultipartTextMessageInternal( - String destinationAddress, String scAddress, List parts, - List sentIntents, List deliveryIntents, - boolean persistMessage, int priority, boolean expectMore, int validityPeriod) { - if (TextUtils.isEmpty(destinationAddress)) { - throw new IllegalArgumentException("Invalid destinationAddress"); - } - if (parts == null || parts.size() < 1) { - throw new IllegalArgumentException("Invalid message body"); - } - - if (priority < 0x00 || priority > 0x03) { - throw new IllegalArgumentException("Invalid priority"); - } - - if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) { - throw new IllegalArgumentException("Invalid validity period"); - } - - if (parts.size() > 1) { - try { - ISms iccISms = getISmsServiceOrThrow(); - if (iccISms != null) { - iccISms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(), - ActivityThread.currentPackageName(), destinationAddress, scAddress, - parts, sentIntents, deliveryIntents, persistMessage, priority, - expectMore, validityPeriod); - } - } catch (RemoteException ex) { - // ignore it - } - } else { - PendingIntent sentIntent = null; - PendingIntent deliveryIntent = null; - if (sentIntents != null && sentIntents.size() > 0) { - sentIntent = sentIntents.get(0); - } - if (deliveryIntents != null && deliveryIntents.size() > 0) { - deliveryIntent = deliveryIntents.get(0); - } - sendTextMessageInternal(destinationAddress, scAddress, parts.get(0), - sentIntent, deliveryIntent, persistMessage, priority, expectMore, - validityPeriod); - } - } - - /** - * Send a multi-part text based SMS without writing it into the SMS Provider. - * - *

Requires Permission: - * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier - * privileges. - *

- * - * @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList, - * ArrayList, int, boolean, int) - * @hide - **/ - public void sendMultipartTextMessageWithoutPersisting( - String destinationAddress, String scAddress, List parts, - List sentIntents, List deliveryIntents, - int priority, boolean expectMore, int validityPeriod) { - sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, - deliveryIntents, false /* persistMessage*/, priority, expectMore, - validityPeriod); - } - - /** * Send a data based SMS to a specific application port. * *

Note: Using this method requires that your app has the @@ -1249,7 +1009,7 @@ public final class SmsManager { * getAllMessagesFromIcc * @return ArrayList of SmsMessage objects. */ - private ArrayList createMessageListFromRawRecords(List records) { + private static ArrayList createMessageListFromRawRecords(List records) { ArrayList messages = new ArrayList(); if (records != null) { int count = records.size(); @@ -1257,8 +1017,7 @@ public final class SmsManager { SmsRawData data = records.get(i); // List contains all records, including "free" records (null) if (data != null) { - SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes(), - getSubscriptionId()); + SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes()); if (sms != null) { messages.add(sms); } diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index a5d67c60e40b0..df41233559a12 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -270,31 +270,6 @@ public class SmsMessage { } } - /** - * Create an SmsMessage from an SMS EF record. - * - * @param index Index of SMS record. This should be index in ArrayList - * returned by SmsManager.getAllMessagesFromSim + 1. - * @param data Record data. - * @param subId Subscription Id of the SMS - * @return An SmsMessage representing the record. - * - * @hide - */ - public static SmsMessage createFromEfRecord(int index, byte[] data, int subId) { - SmsMessageBase wrappedMessage; - - if (isCdmaVoice(subId)) { - wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord( - index, data); - } else { - wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord( - index, data); - } - - return wrappedMessage != null ? new SmsMessage(wrappedMessage) : null; - } - /** * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the * length in bytes (not hex chars) less the SMSC header @@ -847,7 +822,6 @@ public class SmsMessage { int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(subId); return (PHONE_TYPE_CDMA == activePhone); } - /** * Decide if the carrier supports long SMS. * {@hide} diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl index a4eb424ab66e0..fe37531c06112 100644 --- a/telephony/java/com/android/internal/telephony/ISms.aidl +++ b/telephony/java/com/android/internal/telephony/ISms.aidl @@ -186,57 +186,6 @@ interface ISms { in String destAddr, in String scAddr, in String text, in PendingIntent sentIntent, in PendingIntent deliveryIntent, in boolean persistMessage); - /** - * Send an SMS with options using Subscription Id. - * - * @param subId the subId on which the SMS has to be sent. - * @param destAddr the address to send the message to - * @param scAddr the SMSC to send the message through, or NULL for the - * default SMSC - * @param text the body of the message to send - * @param sentIntent if not NULL this PendingIntent is - * broadcast when the message is sucessfully sent, or failed. - * The result code will be Activity.RESULT_OK for success, - * or one of these errors:
- * RESULT_ERROR_GENERIC_FAILURE
- * RESULT_ERROR_RADIO_OFF
- * RESULT_ERROR_NULL_PDU
- * For RESULT_ERROR_GENERIC_FAILURE the sentIntent may include - * the extra "errorCode" containing a radio technology specific value, - * generally only useful for troubleshooting.
- * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applications, - * which cause smaller number of SMS to be sent in checking period. - * @param deliveryIntent if not NULL this PendingIntent is - * broadcast when the message is delivered to the recipient. The - * raw pdu of the status report is in the extended data ("pdu"). - * @param persistMessageForNonDefaultSmsApp whether the sent message should - * be automatically persisted in the SMS db. It only affects messages sent - * by a non-default SMS app. Currently only the carrier app can set this - * parameter to false to skip auto message persistence. - * @param priority Priority level of the message - * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1 - * --------------------------------- - * PRIORITY | Level of Priority - * --------------------------------- - * '00' | Normal - * '01' | Interactive - * '10' | Urgent - * '11' | Emergency - * ---------------------------------- - * Any Other values included Negative considered as Invalid Priority Indicator of the message. - * @param expectMore is a boolean to indicate the sending message is multi segmented or not. - * @param validityPeriod Validity Period of the message in mins. - * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1. - * Validity Period(Minimum) -> 5 mins - * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks). - * Any Other values included Negative considered as Invalid Validity Period of the message. - */ - void sendTextForSubscriberWithOptions(in int subId, String callingPkg, in String destAddr, - in String scAddr, in String text, in PendingIntent sentIntent, - in PendingIntent deliveryIntent, in boolean persistMessageForNonDefaultSmsApp, - in int priority, in boolean expectMore, in int validityPeriod); - /** * Inject an SMS PDU into the android platform. * @@ -284,56 +233,6 @@ interface ISms { in List parts, in List sentIntents, in List deliveryIntents, in boolean persistMessageForNonDefaultSmsApp); - /** - * Send a multi-part text based SMS with options using Subscription Id. - * - * @param subId the subId on which the SMS has to be sent. - * @param destinationAddress the address to send the message to - * @param scAddress is the service center address or null to use - * the current default SMSC - * @param parts an ArrayList of strings that, in order, - * comprise the original message - * @param sentIntents if not null, an ArrayList of - * PendingIntents (one for each message part) that is - * broadcast when the corresponding message part has been sent. - * The result code will be Activity.RESULT_OK for success, - * or one of these errors: - * RESULT_ERROR_GENERIC_FAILURE - * RESULT_ERROR_RADIO_OFF - * RESULT_ERROR_NULL_PDU. - * @param deliveryIntents if not null, an ArrayList of - * PendingIntents (one for each message part) that is - * broadcast when the corresponding message part has been delivered - * to the recipient. The raw pdu of the status report is in the - * extended data ("pdu"). - * @param persistMessageForNonDefaultSmsApp whether the sent message should - * be automatically persisted in the SMS db. It only affects messages sent - * by a non-default SMS app. Currently only the carrier app can set this - * parameter to false to skip auto message persistence. - * @param priority Priority level of the message - * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1 - * --------------------------------- - * PRIORITY | Level of Priority - * --------------------------------- - * '00' | Normal - * '01' | Interactive - * '10' | Urgent - * '11' | Emergency - * ---------------------------------- - * Any Other values included Negative considered as Invalid Priority Indicator of the message. - * @param expectMore is a boolean to indicate the sending message is multi segmented or not. - * @param validityPeriod Validity Period of the message in mins. - * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1. - * Validity Period(Minimum) -> 5 mins - * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks). - * Any Other values included Negative considered as Invalid Validity Period of the message. - */ - void sendMultipartTextForSubscriberWithOptions(in int subId, String callingPkg, - in String destinationAddress, in String scAddress, in List parts, - in List sentIntents, in List deliveryIntents, - in boolean persistMessageForNonDefaultSmsApp, in int priority, in boolean expectMore, - in int validityPeriod); - /** * Enable reception of cell broadcast (SMS-CB) messages with the given * message identifier and RAN type. The RAN type specify this message ID diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index 14c5f4bebebb2..7a53ef63e2a83 100644 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -99,15 +99,6 @@ public class SmsMessage extends SmsMessageBase { private static final int RETURN_NO_ACK = 0; private static final int RETURN_ACK = 1; - /** - * Supported priority modes for CDMA SMS messages - * (See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1) - */ - private static final int PRIORITY_NORMAL = 0x0; - private static final int PRIORITY_INTERACTIVE = 0x1; - private static final int PRIORITY_URGENT = 0x2; - private static final int PRIORITY_EMERGENCY = 0x3; - private SmsEnvelope mEnvelope; private BearerData mBearerData; @@ -220,26 +211,6 @@ public class SmsMessage extends SmsMessageBase { */ public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message, boolean statusReportRequested, SmsHeader smsHeader) { - return getSubmitPdu(scAddr, destAddr, message, statusReportRequested, smsHeader, -1); - } - - /** - * Get an SMS-SUBMIT PDU for a destination address and a message - * - * @param scAddr Service Centre address. Null means use default. - * @param destAddr Address of the recipient. - * @param message String representation of the message payload. - * @param statusReportRequested Indicates whether a report is requested for this message. - * @param smsHeader Array containing the data for the User Data Header, preceded - * by the Element Identifiers. - * @param priority Priority level of the message - * @return a SubmitPdu containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. - * @hide - */ - public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message, - boolean statusReportRequested, SmsHeader smsHeader, int priority) { /** * TODO(cleanup): Do we really want silent failure like this? @@ -253,7 +224,7 @@ public class SmsMessage extends SmsMessageBase { UserData uData = new UserData(); uData.payloadStr = message; uData.userDataHeader = smsHeader; - return privateGetSubmitPdu(destAddr, statusReportRequested, uData, priority); + return privateGetSubmitPdu(destAddr, statusReportRequested, uData); } /** @@ -310,22 +281,6 @@ public class SmsMessage extends SmsMessageBase { return privateGetSubmitPdu(destAddr, statusReportRequested, userData); } - /** - * Get an SMS-SUBMIT PDU for a data message to a destination address & port - * - * @param destAddr the address of the destination for the message - * @param userData the data for the message - * @param statusReportRequested Indicates whether a report is requested for this message. - * @param priority Priority level of the message - * @return a SubmitPdu containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. - */ - public static SubmitPdu getSubmitPdu(String destAddr, UserData userData, - boolean statusReportRequested, int priority) { - return privateGetSubmitPdu(destAddr, statusReportRequested, userData, priority); - } - /** * Note: This function is a GSM specific functionality which is not supported in CDMA mode. */ @@ -809,15 +764,6 @@ public class SmsMessage extends SmsMessageBase { */ private static SubmitPdu privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested, UserData userData) { - return privateGetSubmitPdu(destAddrStr, statusReportRequested, userData, -1); - } - - /** - * Creates BearerData and Envelope from parameters for a Submit SMS. - * @return byte stream for SubmitPdu. - */ - private static SubmitPdu privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested, - UserData userData, int priority) { /** * TODO(cleanup): give this function a more meaningful name. @@ -846,10 +792,6 @@ public class SmsMessage extends SmsMessageBase { bearerData.userAckReq = false; bearerData.readAckReq = false; bearerData.reportReq = false; - if (priority >= PRIORITY_NORMAL && priority <= PRIORITY_EMERGENCY) { - bearerData.priorityIndicatorSet = true; - bearerData.priority = priority; - } bearerData.userData = userData; diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index 4f5bfa9191356..1ca19e01d6c8b 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -89,18 +89,6 @@ public class SmsMessage extends SmsMessageBase { private int mVoiceMailCount = 0; - private static final int VALIDITY_PERIOD_FORMAT_NONE = 0x00; - private static final int VALIDITY_PERIOD_FORMAT_ENHANCED = 0x01; - private static final int VALIDITY_PERIOD_FORMAT_RELATIVE = 0x02; - private static final int VALIDITY_PERIOD_FORMAT_ABSOLUTE = 0x03; - - //Validity Period min - 5 mins - private static final int VALIDITY_PERIOD_MIN = 5; - //Validity Period max - 63 weeks - private static final int VALIDITY_PERIOD_MAX = 635040; - - private static final int INVALID_VALIDITY_PERIOD = -1; - public static class SubmitPdu extends SubmitPduBase { } @@ -213,45 +201,6 @@ public class SmsMessage extends SmsMessageBase { return len - smscLen - 1; } - /** - * Get Encoded Relative Validty Period Value from Validity period in mins. - * - * @param validityPeriod Validity period in mins. - * - * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1. - * ||relValidityPeriod (TP-VP) || || validityPeriod || - * - * 0 to 143 ---> (TP-VP + 1) x 5 minutes - * - * 144 to 167 ---> 12 hours + ((TP-VP -143) x 30 minutes) - * - * 168 to 196 ---> (TP-VP - 166) x 1 day - * - * 197 to 255 ---> (TP-VP - 192) x 1 week - * - * @return relValidityPeriod Encoded Relative Validity Period Value. - * @hide - */ - public static int getRelativeValidityPeriod(int validityPeriod) { - int relValidityPeriod = INVALID_VALIDITY_PERIOD; - - if (validityPeriod < VALIDITY_PERIOD_MIN || validityPeriod > VALIDITY_PERIOD_MAX) { - Rlog.e(LOG_TAG,"Invalid Validity Period" + validityPeriod); - return relValidityPeriod; - } - - if (validityPeriod <= 720) { - relValidityPeriod = (validityPeriod / 5) - 1; - } else if (validityPeriod <= 1440) { - relValidityPeriod = ((validityPeriod - 720) / 30) + 143; - } else if (validityPeriod <= 43200) { - relValidityPeriod = (validityPeriod / 1440) + 166; - } else if (validityPeriod <= 635040) { - relValidityPeriod = (validityPeriod / 10080) + 192; - } - return relValidityPeriod; - } - /** * Get an SMS-SUBMIT PDU for a destination address and a message * @@ -287,29 +236,6 @@ public class SmsMessage extends SmsMessageBase { String destinationAddress, String message, boolean statusReportRequested, byte[] header, int encoding, int languageTable, int languageShiftTable) { - return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, - header, encoding, languageTable, languageShiftTable, -1); - } - - /** - * Get an SMS-SUBMIT PDU for a destination address and a message using the - * specified encoding. - * - * @param scAddress Service Centre address. Null means use default. - * @param encoding Encoding defined by constants in - * com.android.internal.telephony.SmsConstants.ENCODING_* - * @param languageTable - * @param languageShiftTable - * @param validityPeriod Validity Period of the message in Minutes. - * @return a SubmitPdu containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. - * @hide - */ - public static SubmitPdu getSubmitPdu(String scAddress, - String destinationAddress, String message, - boolean statusReportRequested, byte[] header, int encoding, - int languageTable, int languageShiftTable, int validityPeriod) { // Perform null parameter checks. if (message == null || destinationAddress == null) { @@ -346,19 +272,8 @@ public class SmsMessage extends SmsMessageBase { } SubmitPdu ret = new SubmitPdu(); - - int validityPeriodFormat = VALIDITY_PERIOD_FORMAT_NONE; - int relativeValidityPeriod = INVALID_VALIDITY_PERIOD; - - // TP-Validity-Period-Format (TP-VPF) in 3GPP TS 23.040 V6.8.1 section 9.2.3.3 - //bit 4:3 = 10 - TP-VP field present - relative format - if((relativeValidityPeriod = getRelativeValidityPeriod(validityPeriod)) >= 0) { - validityPeriodFormat = VALIDITY_PERIOD_FORMAT_RELATIVE; - } - - byte mtiByte = (byte)(0x01 | (validityPeriodFormat << 0x03) | - (header != null ? 0x40 : 0x00)); - + // MTI = SMS-SUBMIT, UDHI = header != null + byte mtiByte = (byte)(0x01 | (header != null ? 0x40 : 0x00)); ByteArrayOutputStream bo = getSubmitPduHead( scAddress, destinationAddress, mtiByte, statusReportRequested, ret); @@ -423,11 +338,7 @@ public class SmsMessage extends SmsMessageBase { bo.write(0x08); } - if (validityPeriodFormat == VALIDITY_PERIOD_FORMAT_RELATIVE) { - // ( TP-Validity-Period - relative format) - bo.write(relativeValidityPeriod); - } - + // (no TP-Validity-Period) bo.write(userData, 0, userData.length); ret.encodedMessage = bo.toByteArray(); return ret; @@ -476,24 +387,6 @@ public class SmsMessage extends SmsMessageBase { return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, null); } - /** - * Get an SMS-SUBMIT PDU for a destination address and a message - * - * @param scAddress Service Centre address. Null means use default. - * @param destinationAddress the address of the destination for the message - * @param statusReportRequested staus report of the message Requested - * @param validityPeriod Validity Period of the message in Minutes. - * @return a SubmitPdu containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. - */ - public static SubmitPdu getSubmitPdu(String scAddress, - String destinationAddress, String message, - boolean statusReportRequested, int validityPeriod) { - return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, - null, ENCODING_UNKNOWN, 0, 0, validityPeriod); - } - /** * Get an SMS-SUBMIT PDU for a data message to a destination address & port * From a7fefd1814bba0e290183fdcfa37ca0486ff8db3 Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Mon, 11 Dec 2017 10:45:59 -0800 Subject: [PATCH 032/226] Temporaly ignoring UID check when caller is a system app. The Settings app runs some of its activities in another process, which would crash it in some cases due to a recent security fix on Autofill. This CL temporarily fixes the problem by skipping the security check when the caller is a system app. Test: manual verification using System.out statements, as it cannot be reproduced by CTS because the CTS app is an user app. Bug: 70506888 Change-Id: I08e8a370d93d3473ec5e025afaf3bc6f456e0ab9 (cherry picked from commit 70668582a8e2afae38a976819ec692ec0f8edf16) --- .../android/server/autofill/AutofillManagerServiceImpl.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 7bc63f057f48c..fcb93d252450b 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -42,6 +42,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.Looper; +import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; @@ -491,7 +492,10 @@ final class AutofillManagerServiceImpl { } catch (NameNotFoundException e) { throw new SecurityException("Could not verify UID for " + componentName); } - if (callingUid != packageUid) { + // TODO(b/70506888): allow all system UIDs to call it is too broad, we should call + // something like am.isActivityRunningInProcess(componentName, callingPid), but there is + // no such API yet. + if (callingUid != packageUid && Process.isApplicationUid(callingUid)) { final String[] packages = pm.getPackagesForUid(callingUid); final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid; Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid From bd93404936da1a4229f1dcc0a195562a3798f4de Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Mon, 11 Dec 2017 12:42:26 -0800 Subject: [PATCH 033/226] Always invoke scrim callback Callback needs to be invoked if we're trying to switch to the same state, otherwise the window will never know that we're done fading the keyguard. Change-Id: I6779ecf18fbb23f621731d851ab343b82c3529e3 Fixes: 70481733 Fixes: 70392591 Test: Unlock by tapping on notification Test: Unlock with pin, fingerprint Test: Unlock with fingerprint when pulsing Test: Unlock by tapping on notification when pulsing Test: Open Settings from QS when Maps is SHOW_WHEN_LOCKED Test: runtest -x packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java (cherry picked from commit 19aba8e2e71e52ff4133b1f924a6254aa62ee72f) --- .../systemui/statusbar/phone/ScrimController.java | 4 ++++ .../com/android/systemui/statusbar/phone/StatusBar.java | 4 +++- .../statusbar/phone/StatusBarKeyguardViewManager.java | 2 -- .../systemui/statusbar/phone/ScrimControllerTest.java | 9 +++++++++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 05fcbb0a11b6b..168758f64b60b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -195,6 +195,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, public void transitionTo(ScrimState state, Callback callback) { if (state == mState) { + // Call the callback anyway, unless it's already enqueued + if (callback != null && mCallback != callback) { + callback.onFinished(); + } return; } else if (DEBUG) { Log.d(TAG, "State changed to: " + state); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index c61b7e84a318b..0cfad1805dd2d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -656,7 +656,9 @@ public class StatusBar extends SystemUI implements DemoMode, + "mStatusBarKeyguardViewManager was null"); return; } - mStatusBarKeyguardViewManager.onKeyguardFadedAway(); + if (mKeyguardFadingAway) { + mStatusBarKeyguardViewManager.onKeyguardFadedAway(); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index dacd3d935d0a0..8504d8e5fee52 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -380,8 +380,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mStatusBar.fadeKeyguardWhilePulsing(); wakeAndUnlockDejank(); } else { - mFingerprintUnlockController.startKeyguardFadingAway(); - mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration); boolean staying = mStatusBar.hideKeyguard(); if (!staying) { mStatusBarWindowManager.setKeyguardFadingAway(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index b9f695be90cf4..37dcaa8bd55c5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -229,6 +229,15 @@ public class ScrimControllerTest extends SysuiTestCase { verify(mWakeLock, times(1)).release(); } + @Test + public void testCallbackInvokedOnSameStateTransition() { + mScrimController.transitionTo(ScrimState.UNLOCKED); + mScrimController.finishAnimationsImmediately(); + ScrimController.Callback callback = mock(ScrimController.Callback.class); + mScrimController.transitionTo(ScrimState.UNLOCKED, callback); + verify(callback, times(1)).onFinished(); + } + private void assertScrimTint(ScrimView scrimView, boolean tinted) { final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT; final String name = scrimView == mScrimInFront ? "front" : "back"; From 86325fc412704ed6d4850a1a0263b75eb07af5d2 Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Mon, 11 Dec 2017 14:40:07 -0800 Subject: [PATCH 034/226] Avoid flickering when unlocking with fingerprint Fixes issue where scrim state could be set to KEYGUARD for 1 frame and user would see the notification shade. Change-Id: I577b969f98573fc481e3d7c283eb5b612b7e5cee Fixes: 70210651 Test: Unlock with fingerprint from AoD Test: runtest -x packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java (cherry picked from commit 311eac8292a03ae95207c250aa48d044f7bc6372) --- .../systemui/statusbar/phone/StatusBar.java | 13 +++++++++++-- .../statusbar/phone/StatusBarTest.java | 19 ++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 0cfad1805dd2d..4114ad4365b1c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -5148,7 +5148,15 @@ public class StatusBar extends SystemUI implements DemoMode, Trace.endSection(); } - public void updateScrimController() { + @VisibleForTesting + void updateScrimController() { + Trace.beginSection("StatusBar#updateScrimController"); + + // We don't want to end up in KEYGUARD state when we're unlocking with + // fingerprint from doze. We should cross fade directly from black. + final boolean wakeAndUnlocking = mFingerprintUnlockController.getMode() + == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK; + if (mBouncerShowing) { mScrimController.transitionTo(ScrimState.BOUNCER); } else if (mLaunchCameraOnScreenTurningOn || isInLaunchTransition()) { @@ -5159,11 +5167,12 @@ public class StatusBar extends SystemUI implements DemoMode, // Handled in DozeScrimController#setPulsing } else if (mDozing) { mScrimController.transitionTo(ScrimState.AOD); - } else if (mIsKeyguard) { + } else if (mIsKeyguard && !wakeAndUnlocking) { mScrimController.transitionTo(ScrimState.KEYGUARD); } else { mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback); } + Trace.endSection(); } public boolean isKeyguardShowing() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 5ff90d91b333e..e4c33f11221b0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -26,6 +26,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -107,6 +108,7 @@ public class StatusBarTest extends SysuiTestCase { ScrimController mScrimController; IStatusBarService mBarService; ArrayList mNotificationList; + FingerprintUnlockController mFingerprintUnlockController; private DisplayMetrics mDisplayMetrics = new DisplayMetrics(); @Before @@ -133,6 +135,7 @@ public class StatusBarTest extends SysuiTestCase { when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0)); mNotificationList = mock(ArrayList.class); mScrimController = mock(ScrimController.class); + mFingerprintUnlockController = mock(FingerprintUnlockController.class); IPowerManager powerManagerService = mock(IPowerManager.class); HandlerThread handlerThread = new HandlerThread("TestThread"); handlerThread.start(); @@ -145,7 +148,7 @@ public class StatusBarTest extends SysuiTestCase { mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache, mKeyguardIndicationController, mStackScroller, mHeadsUpManager, mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView, - mBarService, mScrimController); + mBarService, mScrimController, mFingerprintUnlockController); mStatusBar.mContext = mContext; mStatusBar.mComponents = mContext.getComponents(); doAnswer(invocation -> { @@ -538,18 +541,27 @@ public class StatusBarTest extends SysuiTestCase { @Test public void testFingerprintNotification_UpdatesScrims() { mStatusBar.mStatusBarWindowManager = mock(StatusBarWindowManager.class); - mStatusBar.mFingerprintUnlockController = mock(FingerprintUnlockController.class); mStatusBar.mDozeScrimController = mock(DozeScrimController.class); mStatusBar.notifyFpAuthModeChanged(); verify(mScrimController).transitionTo(any(), any()); } + @Test + public void testFingerprintUnlock_UpdatesScrims() { + // Simulate unlocking from AoD with fingerprint. + when(mFingerprintUnlockController.getMode()) + .thenReturn(FingerprintUnlockController.MODE_WAKE_AND_UNLOCK); + mStatusBar.updateScrimController(); + verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any()); + } + static class TestableStatusBar extends StatusBar { public TestableStatusBar(StatusBarKeyguardViewManager man, UnlockMethodCache unlock, KeyguardIndicationController key, NotificationStackScrollLayout stack, HeadsUpManager hum, NotificationData nd, PowerManager pm, SystemServicesProxy ssp, NotificationPanelView panelView, - IStatusBarService barService, ScrimController scrimController) { + IStatusBarService barService, ScrimController scrimController, + FingerprintUnlockController fingerprintUnlockController) { mStatusBarKeyguardViewManager = man; mUnlockMethodCache = unlock; mKeyguardIndicationController = key; @@ -563,6 +575,7 @@ public class StatusBarTest extends SysuiTestCase { mBarService = barService; mWakefulnessLifecycle = createAwakeWakefulnessLifecycle(); mScrimController = scrimController; + mFingerprintUnlockController = fingerprintUnlockController; } private WakefulnessLifecycle createAwakeWakefulnessLifecycle() { From 15fbd4d4ba418d46ee8b38af22e3567721863b16 Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Tue, 12 Dec 2017 16:07:55 +0100 Subject: [PATCH 035/226] DisplayCutout: Only dispatch to apps requesting it Fixes a compatibility issues, where apps that were not expecting a cutout were dispatched one anyway, which caused the WindowInsets dispatch to continue down the hierarchy even though the SystemInsets were consumed by the app. To avoid this, we pre-emptively consume the cutout for any apps that did not request to be laid out in the cutout area. This is safe, because for apps that don't request it, the status bar will take care of consuming it, or they won't be laid out in the cutout at all. If apps still need to know where the cutout is, they can query for it via View.getRootWindowInsets(). Fixes: 65689439 Bug: 70490585 Test: atest android.view.cts.DisplayCutoutTest Change-Id: If06674c619f095d4105be1b3a511fb5823b63d2b (cherry picked from commit 2b0dcb3fd220ef02f534188c88451a3530c04396) --- core/java/android/view/ViewRootImpl.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 129a255c19894..c6c42faad6b06 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -20,6 +20,7 @@ import static android.view.Display.INVALID_DISPLAY; import static android.view.View.PFLAG_DRAW_ANIMATION; import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER; import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM; +import static android.view.WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; @@ -1586,7 +1587,15 @@ public final class ViewRootImpl implements ViewParent, } void dispatchApplyInsets(View host) { - host.dispatchApplyWindowInsets(getWindowInsets(true /* forceConstruct */)); + WindowInsets insets = getWindowInsets(true /* forceConstruct */); + final boolean layoutInCutout = + (mWindowAttributes.flags2 & FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA) != 0; + if (!layoutInCutout) { + // Window is either not laid out in cutout or the status bar inset takes care of + // clearing the cutout, so we don't need to dispatch the cutout to the hierarchy. + insets = insets.consumeCutout(); + } + host.dispatchApplyWindowInsets(insets); } private static boolean shouldUseDisplaySize(final WindowManager.LayoutParams lp) { From b84f2083e89e8c114267784ea87533863dcdc9f9 Mon Sep 17 00:00:00 2001 From: Andrii Kulian Date: Wed, 13 Dec 2017 14:46:43 -0800 Subject: [PATCH 036/226] Disable object pool for lifecycler In the case when client lives in system_server, we shouldn't recycle the lifecycle transaction objects to the pool. Disabled this feature for now. Bug: 70616950 Test: Manual Change-Id: Ib70096aa35accfeb93108f60e3cfe8440b9cd6e8 (cherry picked from commit 3b8b3c34c216838cc5949b1710d415ed9dbbedcd) --- .../src/android/app/servertransaction/ObjectPoolTests.java | 3 ++- .../java/com/android/server/am/ClientLifecycleManager.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java index aefc47e955127..c19a343957c06 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -42,7 +42,8 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @SmallTest -@Presubmit +// TODO: b/70616950 +//@Presubmit public class ObjectPoolTests { // 1. Check if two obtained objects from pool are not the same. diff --git a/services/core/java/com/android/server/am/ClientLifecycleManager.java b/services/core/java/com/android/server/am/ClientLifecycleManager.java index cc70f18dc7472..014f7086efa31 100644 --- a/services/core/java/com/android/server/am/ClientLifecycleManager.java +++ b/services/core/java/com/android/server/am/ClientLifecycleManager.java @@ -43,7 +43,8 @@ class ClientLifecycleManager { */ void scheduleTransaction(ClientTransaction transaction) throws RemoteException { transaction.schedule(); - transaction.recycle(); + // TODO: b/70616950 + //transaction.recycle(); } /** From 5cc27ac6c4a36e60af84e33068f329838aa3858c Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 13 Dec 2017 13:48:50 -0800 Subject: [PATCH 037/226] Keyguard can be on even if not showing It is possible for keyguard to be in "restricted input" state even if isKeyguardShowingAndNotOccluded returns false. The previous CL to deprecate inKeyguardRestrictedInputMode has also changed the behaviour of isKeyguardLocked. Return to the old behaviour here, but keep the API deprecated. Bug: 70411251 Test: 1) add Google account to phone 2) Enable google assistant and set up active edge to trigger the assistant 3) Ensure phone is in the always-on-display mode 4) Squeeze the phone to trigger the assistant. Previously, the assistant would not launch. The expected behaviour is that assistant gets launched. Without this change, display will turn on but the assistant will not get launched. Change-Id: Ic299f3fc223ebdc3b0f6eea97cb4a89a3142e44a (cherry picked from commit 35fbb312e9a185d3d6ed20697b486faae8409463) --- .../com/android/server/policy/PhoneWindowManager.java | 9 ++++++++- .../android/server/policy/WindowManagerPolicy.java | 11 +++++++++++ .../android/server/wm/TestWindowManagerPolicy.java | 5 +++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 5f03dd262fa43..7c75f777ff1d3 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3334,7 +3334,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } boolean keyguardOn() { - return isKeyguardShowingAndNotOccluded(); + return isKeyguardShowingAndNotOccluded() || inKeyguardRestrictedKeyInputMode(); } private static final int[] WINDOW_TYPES_WHERE_HOME_DOESNT_WORK = { @@ -6876,6 +6876,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { return mKeyguardOccluded; } + /** {@inheritDoc} */ + @Override + public boolean inKeyguardRestrictedKeyInputMode() { + if (mKeyguardDelegate == null) return false; + return mKeyguardDelegate.isInputRestricted(); + } + @Override public void dismissKeyguardLw(IKeyguardDismissCallback callback) { if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) { diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index bcb7212b620c7..cfe4088781b4f 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -1359,6 +1359,17 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { */ public boolean isKeyguardTrustedLw(); + /** + * inKeyguardRestrictedKeyInputMode + * + * If keyguard screen is showing or in restricted key input mode (i.e. in + * keyguard password emergency screen). When in such mode, certain keys, + * such as the Home key and the right soft keys, don't work. + * + * @return true if in keyguard restricted input mode. + */ + public boolean inKeyguardRestrictedKeyInputMode(); + /** * Ask the policy to dismiss the keyguard, if it is currently shown. * diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index 481c898414ac5..c73534196f456 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -410,6 +410,11 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { return false; } + @Override + public boolean inKeyguardRestrictedKeyInputMode() { + return false; + } + @Override public void dismissKeyguardLw(@Nullable IKeyguardDismissCallback callback) { } From e859580ada70b96107d9530f4c80d23863867a9d Mon Sep 17 00:00:00 2001 From: Tony Mak Date: Thu, 14 Dec 2017 12:40:07 +0000 Subject: [PATCH 038/226] clearCallingIdentity before calling into getPackageUidAsUser Fix: 70585244 Test: Enable any accessibility service -> inflate work profile -> Tap on any work app -> no longer crash Test: cts-tradefed run cts-dev --module DevicePolicyManager --test com.android.cts.devicepolicy.CrossProfileAppsHostSideTest.testPrimaryUserToManagedProfile Change-Id: I80d18f4e2ab76a228cb0aa2c8312c323a9b5c84d (cherry picked from commit 1232d583bb54fac88b510b72d8dbc79ced36f0f4) --- .../server/accessibility/AccessibilityManagerService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index ac0cdd7cf99c6..f417f0b4bf59a 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -2962,11 +2962,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private boolean isValidPackageForUid(String packageName, int uid) { + final long token = Binder.clearCallingIdentity(); try { return uid == mPackageManager.getPackageUidAsUser( packageName, UserHandle.getUserId(uid)); } catch (PackageManager.NameNotFoundException e) { return false; + } finally { + Binder.restoreCallingIdentity(token); } } From b2658a46e615bf2b8722620bcb0e685387fde5ed Mon Sep 17 00:00:00 2001 From: Sungsoo Lim Date: Thu, 7 Dec 2017 20:14:24 +0900 Subject: [PATCH 039/226] Use broadcast receiver to update BT A2DP status Audio routes observer in AudioService could have several seconds delay. To mitigate the issue because of the delay, this CL uses a BroadcastReceiver to get the Bluetooth device connection events. Bug: 70175199 Test: manually and passed Media CTS Change-Id: Ic1e9db1b8d5759070962011d7f2fc4f940ce8497 (cherry picked from commit 7dafa9acffbddb37bc227fcc2312f35eb2de0b1d) --- .../server/media/MediaRouterService.java | 66 +++++++++++-------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index 0b089fbc61292..384efdda88d75 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -21,6 +21,9 @@ import com.android.server.Watchdog; import android.annotation.NonNull; import android.app.ActivityManager; +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -96,21 +99,23 @@ public final class MediaRouterService extends IMediaRouterService.Stub private final ArrayMap mAllClientRecords = new ArrayMap(); private int mCurrentUserId = -1; - private boolean mGlobalBluetoothA2dpOn = false; private final IAudioService mAudioService; private final AudioPlayerStateMonitor mAudioPlayerStateMonitor; private final Handler mHandler = new Handler(); - private final AudioRoutesInfo mAudioRoutesInfo = new AudioRoutesInfo(); private final IntArray mActivePlayerMinPriorityQueue = new IntArray(); private final IntArray mActivePlayerUidMinPriorityQueue = new IntArray(); + private final BroadcastReceiver mReceiver = new MediaRouterServiceBroadcastReceiver(); + BluetoothDevice mBluetoothDevice; + int mAudioRouteMainType = AudioRoutesInfo.MAIN_SPEAKER; + boolean mGlobalBluetoothA2dpOn = false; + public MediaRouterService(Context context) { mContext = context; Watchdog.getInstance().addMonitor(this); mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); - mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(); mAudioPlayerStateMonitor.registerListener( new AudioPlayerStateMonitor.OnAudioPlayerActiveStateChangedListener() { @@ -170,44 +175,30 @@ public final class MediaRouterService extends IMediaRouterService.Stub @Override public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) { synchronized (mLock) { - if (newRoutes.mainType != mAudioRoutesInfo.mainType) { + if (newRoutes.mainType != mAudioRouteMainType) { if ((newRoutes.mainType & (AudioRoutesInfo.MAIN_HEADSET | AudioRoutesInfo.MAIN_HEADPHONES | AudioRoutesInfo.MAIN_USB)) == 0) { // headset was plugged out. - mGlobalBluetoothA2dpOn = newRoutes.bluetoothName != null; + mGlobalBluetoothA2dpOn = mBluetoothDevice != null; } else { // headset was plugged in. mGlobalBluetoothA2dpOn = false; } - mAudioRoutesInfo.mainType = newRoutes.mainType; + mAudioRouteMainType = newRoutes.mainType; } - if (!TextUtils.equals( - newRoutes.bluetoothName, mAudioRoutesInfo.bluetoothName)) { - if (newRoutes.bluetoothName == null) { - // BT was disconnected. - mGlobalBluetoothA2dpOn = false; - } else { - // BT was connected or changed. - mGlobalBluetoothA2dpOn = true; - } - mAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName; - } - // Although a Bluetooth device is connected before a new audio playback is - // started, dispatchAudioRoutChanged() can be called after - // onAudioPlayerActiveStateChanged(). That causes restoreBluetoothA2dp() - // is called before mGlobalBluetoothA2dpOn is updated. - // Calling restoreBluetoothA2dp() here could prevent that. - restoreBluetoothA2dp(); + // The new audio routes info could be delivered with several seconds delay. + // In order to avoid such delay, Bluetooth device info will be updated + // via MediaRouterServiceBroadcastReceiver. } } }); } catch (RemoteException e) { Slog.w(TAG, "RemoteException in the audio service."); } - synchronized (mLock) { - mGlobalBluetoothA2dpOn = (audioRoutes != null && audioRoutes.bluetoothName != null); - } + + IntentFilter intentFilter = new IntentFilter(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); + context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null); } public void systemRunning() { @@ -415,14 +406,12 @@ public final class MediaRouterService extends IMediaRouterService.Stub void restoreBluetoothA2dp() { try { - boolean btConnected = false; boolean a2dpOn = false; synchronized (mLock) { - btConnected = mAudioRoutesInfo.bluetoothName != null; a2dpOn = mGlobalBluetoothA2dpOn; } // We don't need to change a2dp status when bluetooth is not connected. - if (btConnected) { + if (mBluetoothDevice != null) { Slog.v(TAG, "restoreBluetoothA2dp(" + a2dpOn + ")"); mAudioService.setBluetoothA2dpOn(a2dpOn); } @@ -661,6 +650,25 @@ public final class MediaRouterService extends IMediaRouterService.Stub return false; } + final class MediaRouterServiceBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) { + int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, + BluetoothProfile.STATE_DISCONNECTED); + if (state == BluetoothProfile.STATE_DISCONNECTED) { + mGlobalBluetoothA2dpOn = false; + mBluetoothDevice = null; + } else if (state == BluetoothProfile.STATE_CONNECTED) { + mGlobalBluetoothA2dpOn = true; + mBluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + // To ensure that BT A2DP is on, call restoreBluetoothA2dp(). + restoreBluetoothA2dp(); + } + } + } + } + /** * Information about a particular client of the media router. * The contents of this object is guarded by mLock. From 9f8d8299a1bea4ed515d608b3814722f3f64754a Mon Sep 17 00:00:00 2001 From: Jason Monk Date: Thu, 21 Dec 2017 10:39:17 -0500 Subject: [PATCH 040/226] Workaround apps that are doing very very bad things Disable AppComponentFactory for them. Test: manual Bug: 70776434 Change-Id: Iccfc47af360b719578f0ab9771849a822118518d (cherry picked from commit 24d12a327d82df29238f72747783283147c57bac) --- core/java/android/app/Instrumentation.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 41324d0813b8b..b469de56d857c 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1209,6 +1209,11 @@ public class Instrumentation { } private AppComponentFactory getFactory(String pkg) { + if (mThread == null) { + Log.e(TAG, "Uninitialized ActivityThread, likely app-created Instrumentation," + + " disabling AppComponentFactory", new Throwable()); + return AppComponentFactory.DEFAULT; + } LoadedApk loadedApk = mThread.peekLoadedApk(pkg, true); // This is in the case of starting up "android". if (loadedApk == null) loadedApk = mThread.getSystemContext().mLoadedApk; From 3873eead0d50a4a90b78c8dad6b8edf7d910cff4 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 3 Jan 2018 11:45:47 -0800 Subject: [PATCH 041/226] Fix regression in PiP app-ops listener. Bug: 71526100 Test: Launch settings for PiP app, ensure that disabling app-ops setting also dismisses PiP app. Change-Id: I7cb9d131ad38ce8ead06889f52f1ee82d98f5d1d (cherry picked from commit 95bccdc6eab16afa40121f17dec0dd620c21b02c) --- .../systemui/pip/phone/PipAppOpsListener.java | 89 +++++++++++++++++++ .../systemui/pip/phone/PipManager.java | 5 ++ 2 files changed, 94 insertions(+) create mode 100644 packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java new file mode 100644 index 0000000000000..f0e4ccc139cad --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.pip.phone; + +import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE; + +import android.app.AppOpsManager; +import android.app.AppOpsManager.OnOpChangedListener; +import android.app.IActivityManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.util.Pair; + +public class PipAppOpsListener { + private static final String TAG = PipAppOpsListener.class.getSimpleName(); + + private Context mContext; + private IActivityManager mActivityManager; + private AppOpsManager mAppOpsManager; + + private PipMotionHelper mMotionHelper; + + private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() { + @Override + public void onOpChanged(String op, String packageName) { + try { + // Dismiss the PiP once the user disables the app ops setting for that package + final Pair topPipActivityInfo = + PipUtils.getTopPinnedActivity(mContext, mActivityManager); + if (topPipActivityInfo.first != null) { + final ApplicationInfo appInfo = mContext.getPackageManager() + .getApplicationInfoAsUser(packageName, 0, topPipActivityInfo.second); + if (appInfo.packageName.equals(topPipActivityInfo.first.getPackageName()) && + mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid, + packageName) != MODE_ALLOWED) { + mMotionHelper.dismissPip(); + } + } + } catch (NameNotFoundException e) { + // Unregister the listener if the package can't be found + unregisterAppOpsListener(); + } + } + }; + + public PipAppOpsListener(Context context, IActivityManager activityManager, + PipMotionHelper motionHelper) { + mContext = context; + mActivityManager = activityManager; + mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + mMotionHelper = motionHelper; + } + + public void onActivityPinned(String packageName) { + // Register for changes to the app ops setting for this package while it is in PiP + registerAppOpsListener(packageName); + } + + public void onActivityUnpinned() { + // Unregister for changes to the previously PiP'ed package + unregisterAppOpsListener(); + } + + private void registerAppOpsListener(String packageName) { + mAppOpsManager.startWatchingMode(OP_PICTURE_IN_PICTURE, packageName, + mAppOpsChangedListener); + } + + private void unregisterAppOpsListener() { + mAppOpsManager.stopWatchingMode(mAppOpsChangedListener); + } +} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index dce3e24307e2b..36531bb727a4a 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -65,6 +65,7 @@ public class PipManager implements BasePipManager { private PipMenuActivityController mMenuController; private PipMediaController mMediaController; private PipTouchHandler mTouchHandler; + private PipAppOpsListener mAppOpsListener; /** * Handler for system task stack changes. @@ -75,6 +76,7 @@ public class PipManager implements BasePipManager { mTouchHandler.onActivityPinned(); mMediaController.onActivityPinned(); mMenuController.onActivityPinned(); + mAppOpsListener.onActivityPinned(packageName); SystemServicesProxy.getInstance(mContext).setPipVisibility(true); } @@ -87,6 +89,7 @@ public class PipManager implements BasePipManager { final int userId = topActivity != null ? topPipActivityInfo.second : 0; mMenuController.onActivityUnpinned(); mTouchHandler.onActivityUnpinned(topActivity); + mAppOpsListener.onActivityUnpinned(); SystemServicesProxy.getInstance(mContext).setPipVisibility(topActivity != null); } @@ -177,6 +180,8 @@ public class PipManager implements BasePipManager { mInputConsumerController); mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController, mInputConsumerController); + mAppOpsListener = new PipAppOpsListener(context, mActivityManager, + mTouchHandler.getMotionHelper()); EventBus.getDefault().register(this); } From e6c890c57f4183a10d6b03d9f3ab4e096246180c Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Wed, 3 Jan 2018 15:54:43 +0100 Subject: [PATCH 042/226] Fix issue with 0 duration animations If the animation length was 0, it was possible that the finish runnable is run before applying the pending transaction to reparent the surface onto the leash. In that case, the reparent to the leash will be executed after, taking precedence. Then, the leash gets destroyed, and we loose the surface, leading to all kinds of crashes. Test: Disable animation duration scale, open a couple of apps, observe no crash. Test: go/wm-smoke Change-Id: I04db7b7c1c3295779b8afead97d7850f808f9081 Fixes: 71499373 (cherry picked from commit a384403e1297bd61c76907631c31bf29d33fd6da) --- .../com/android/server/wm/DisplayContent.java | 2 + .../android/server/wm/SurfaceAnimator.java | 38 +++++++++++-------- .../com/android/server/wm/WindowAnimator.java | 27 +++++++++++++ .../server/wm/SurfaceAnimatorTest.java | 13 +++++++ .../android/server/wm/WindowTestsBase.java | 1 + 5 files changed, 65 insertions(+), 16 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index d053015cea821..63dfbc2c25b65 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1923,6 +1923,8 @@ class DisplayContent extends WindowContainer { + if (anim != mAnimation) { + // Callback was from another animation - ignore. + return; + } + final Transaction t = new Transaction(); + SurfaceControl.openTransaction(); + try { + reset(t, true /* destroyLeash */); + animationFinishedCallback.run(); + } finally { + SurfaceControl.mergeToGlobalTransaction(t); + SurfaceControl.closeTransaction(); + } + }); } }; } diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 8bceb640aa500..7295873754214 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -35,6 +35,7 @@ import com.android.server.AnimationThread; import com.android.server.policy.WindowManagerPolicy; import java.io.PrintWriter; +import java.util.ArrayList; /** * Singleton class that carries out the animations and Surface operations in a separate task @@ -87,6 +88,12 @@ public class WindowAnimator { */ private boolean mAnimationFrameCallbackScheduled; + /** + * A list of runnable that need to be run after {@link WindowContainer#prepareSurfaces} is + * executed and the corresponding transaction is closed and applied. + */ + private final ArrayList mAfterPrepareSurfacesRunnables = new ArrayList<>(); + WindowAnimator(final WindowManagerService service) { mService = service; mContext = service.mContext; @@ -262,6 +269,7 @@ public class WindowAnimator { mService.destroyPreservedSurfaceLocked(); mService.mWindowPlacerLocked.destroyPendingSurfaces(); + executeAfterPrepareSurfacesRunnables(); if (DEBUG_WINDOW_TRACE) { Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating @@ -425,4 +433,23 @@ public class WindowAnimator { void orAnimating(boolean animating) { mAnimating |= animating; } + + /** + * Adds a runnable to be executed after {@link WindowContainer#prepareSurfaces} is called and + * the corresponding transaction is closed and applied. + */ + void addAfterPrepareSurfacesRunnable(Runnable r) { + mAfterPrepareSurfacesRunnables.add(r); + scheduleAnimation(); + } + + private void executeAfterPrepareSurfacesRunnables() { + + // Traverse in order they were added. + final int size = mAfterPrepareSurfacesRunnables.size(); + for (int i = 0; i < size; i++) { + mAfterPrepareSurfacesRunnables.get(i).run(); + } + mAfterPrepareSurfacesRunnables.clear(); + } } diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java index 96ff461950025..3da560705ef45 100644 --- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java @@ -45,6 +45,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; /** * Test class for {@link SurfaceAnimatorTest}. @@ -82,6 +83,7 @@ public class SurfaceAnimatorTest extends WindowTestsBase { verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture()); callbackCaptor.getValue().onAnimationFinished(mSpec); + waitUntilPrepareSurfaces(); assertNotAnimating(mAnimatable); assertTrue(mAnimatable.mFinishedCallbackCalled); assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash)); @@ -104,11 +106,13 @@ public class SurfaceAnimatorTest extends WindowTestsBase { // First animation was finished, but this shouldn't cancel the second animation callbackCaptor.getValue().onAnimationFinished(mSpec); + waitUntilPrepareSurfaces(); assertTrue(mAnimatable.mSurfaceAnimator.isAnimating()); // Second animation was finished verify(mSpec2).startAnimation(any(), any(), callbackCaptor.capture()); callbackCaptor.getValue().onAnimationFinished(mSpec2); + waitUntilPrepareSurfaces(); assertNotAnimating(mAnimatable); assertTrue(mAnimatable.mFinishedCallbackCalled); } @@ -160,6 +164,7 @@ public class SurfaceAnimatorTest extends WindowTestsBase { assertEquals(leash, mAnimatable2.mSurfaceAnimator.mLeash); assertFalse(mAnimatable.mPendingDestroySurfaces.contains(leash)); callbackCaptor.getValue().onAnimationFinished(mSpec); + waitUntilPrepareSurfaces(); assertNotAnimating(mAnimatable2); assertTrue(mAnimatable2.mFinishedCallbackCalled); assertTrue(mAnimatable2.mPendingDestroySurfaces.contains(leash)); @@ -175,6 +180,14 @@ public class SurfaceAnimatorTest extends WindowTestsBase { assertNull(animatable.mSurfaceAnimator.getAnimation()); } + private void waitUntilPrepareSurfaces() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + synchronized (sWm.mWindowMap) { + sWm.mAnimator.addAfterPrepareSurfacesRunnable(latch::countDown); + } + latch.await(); + } + private class MyAnimatable implements Animatable { final SurfaceControl mParent; diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index c699a94db279b..ff840f3aeea91 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -103,6 +103,7 @@ class WindowTestsBase { context.getDisplay().getDisplayInfo(mDisplayInfo); mDisplayContent = createNewDisplay(); + sWm.mAnimator.mInitialized = true; sWm.mDisplayEnabled = true; sWm.mDisplayReady = true; From f43b6aeb90640da98dc9923b8541c57fc9ccf5ec Mon Sep 17 00:00:00 2001 From: chaviw Date: Thu, 28 Dec 2017 11:35:53 -0800 Subject: [PATCH 043/226] Ensure the correct animation bounds for STACK_CLIP_BEFORE Previously the animation clip was intersected with the stack bounds. Instead, the stack bounds should be intersected with the animation clip. Also make sure the stack bounds are offset to 0,0 so they don't take into account position. Test: Minimize primary in split screen. Lock screen and unlock. Minimized screen is now correct crop. Test: Set an app in primary and secondary split screen. Lock screen and unlock. Both primary and secondary should be in the correct place with the correct crop. Test: WindowAnimationSpecTest Change-Id: Ia8b4f77a8e71b63c19e759b8af7de446a20d8a56 (cherry picked from commit 977482aa7407ae9c1b7684627b0133784eaaf137) --- .../com/android/server/wm/AppWindowToken.java | 1 + .../server/wm/WindowAnimationSpec.java | 4 +- .../server/wm/WindowAnimationSpecTest.java | 118 ++++++++++++++++++ 3 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 44d7948b12b62..573715397add7 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -1541,6 +1541,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (stack != null) { stack.getRelativePosition(mTmpPoint); stack.getBounds(mTmpRect); + mTmpRect.offsetTo(0, 0); } final AnimationAdapter adapter = new LocalAnimationAdapter( new WindowAnimationSpec(a, mTmpPoint, mTmpRect, diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java index 986529344a788..031b57b73b055 100644 --- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java +++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java @@ -92,8 +92,8 @@ public class WindowAnimationSpec implements AnimationSpec { t.setFinalCrop(leash, mStackBounds); t.setWindowCrop(leash, tmp.transformation.getClipRect()); } else { - mTmpRect.set(tmp.transformation.getClipRect()); - mTmpRect.intersect(mStackBounds); + mTmpRect.set(mStackBounds); + mTmpRect.intersect(tmp.transformation.getClipRect()); t.setWindowCrop(leash, mTmpRect); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java new file mode 100644 index 0000000000000..9cdef16194ff4 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.wm; + +import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; +import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM; +import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE; + +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.SurfaceControl; +import android.view.animation.Animation; +import android.view.animation.ClipRectAnimation; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for the {@link WindowAnimationSpec} class. + * + * atest FrameworksServicesTests:com.android.server.wm.WindowAnimationSpecTest + */ +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class WindowAnimationSpecTest { + private final SurfaceControl mSurfaceControl = mock(SurfaceControl.class); + private final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class); + private final Animation mAnimation = mock(Animation.class); + private final Rect mStackBounds = new Rect(0, 0, 10, 10); + + @Test + public void testApply_clipNone() { + Rect windowCrop = new Rect(0, 0, 20, 20); + Animation a = new ClipRectAnimation(windowCrop, windowCrop); + WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null, + mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_NONE); + windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); + verify(mTransaction).setWindowCrop(eq(mSurfaceControl), + argThat(rect -> rect.equals(windowCrop))); + } + + @Test + public void testApply_clipAfter() { + WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null, + mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_AFTER_ANIM); + windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); + verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty)); + verify(mTransaction).setFinalCrop(eq(mSurfaceControl), + argThat(rect -> rect.equals(mStackBounds))); + } + + @Test + public void testApply_clipBeforeNoAnimationBounds() { + // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 0, 0) + WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null, + mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM); + windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); + verify(mTransaction).setWindowCrop(eq(mSurfaceControl), + argThat(rect -> rect.equals(mStackBounds))); + } + + @Test + public void testApply_clipBeforeNoStackBounds() { + // Stack bounds is (0, 0, 0, 0) animation clip is (0, 0, 20, 20) + Rect windowCrop = new Rect(0, 0, 20, 20); + Animation a = new ClipRectAnimation(windowCrop, windowCrop); + WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null, + null, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM); + windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); + verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty)); + } + + @Test + public void testApply_clipBeforeSmallerAnimationClip() { + // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 5, 5) + Rect windowCrop = new Rect(0, 0, 5, 5); + Animation a = new ClipRectAnimation(windowCrop, windowCrop); + WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null, + mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM); + windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); + verify(mTransaction).setWindowCrop(eq(mSurfaceControl), + argThat(rect -> rect.equals(windowCrop))); + } + + @Test + public void testApply_clipBeforeSmallerStackClip() { + // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 20, 20) + Rect windowCrop = new Rect(0, 0, 20, 20); + Animation a = new ClipRectAnimation(windowCrop, windowCrop); + WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null, + mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM); + windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); + verify(mTransaction).setWindowCrop(eq(mSurfaceControl), + argThat(rect -> rect.equals(mStackBounds))); + } +} From a274931b8b66b6dedbcf4ed492597055b18a215e Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Mon, 8 Jan 2018 17:33:30 +0100 Subject: [PATCH 044/226] Revert "Avoid setting size/position on Transaction if not needed" Reason: Causes weird issues with transaction reordering Fixes: 71694437 Fixes: 71692657 This reverts commit 88f620534f9fe2c3a32765b2a34d3092ab889b1a. (cherry picked from commit d70feb234f47d6088c751f8c5529a29ac3aa029b) Change-Id: I7bfa40eea88a609611ecb1c9caaaa2b9c7712d8c --- .../core/java/com/android/server/wm/TaskStack.java | 10 +--------- .../java/com/android/server/wm/WindowContainer.java | 6 ------ .../core/java/com/android/server/wm/WindowState.java | 4 +--- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index eb8eae1a95d7d..3ffc7fae5d0a1 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -44,7 +44,6 @@ import static com.android.server.wm.proto.StackProto.WINDOW_CONTAINER; import android.annotation.CallSuper; import android.content.res.Configuration; -import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.os.RemoteException; @@ -146,7 +145,6 @@ public class TaskStack extends WindowContainer implements * For {@link #prepareSurfaces}. */ final Rect mTmpDimBoundsRect = new Rect(); - private final Point mLastSurfaceSize = new Point(); TaskStack(WindowManagerService service, int stackId, StackWindowController controller) { super(service); @@ -745,13 +743,7 @@ public class TaskStack extends WindowContainer implements } final Rect stackBounds = getBounds(); - final int width = stackBounds.width(); - final int height = stackBounds.height(); - if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) { - return; - } - transaction.setSize(mSurfaceControl, width, height); - mLastSurfaceSize.set(width, height); + transaction.setSize(mSurfaceControl, stackBounds.width(), stackBounds.height()); } @Override diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index af314101cd2bd..5d445eff0c922 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -95,7 +95,6 @@ class WindowContainer extends ConfigurationContainer< protected final WindowManagerService mService; private final Point mTmpPos = new Point(); - protected final Point mLastSurfacePosition = new Point(); /** Total number of elements in this subtree, including our own hierarchy element. */ private int mTreeWeight = 1; @@ -1178,12 +1177,7 @@ class WindowContainer extends ConfigurationContainer< } getRelativePosition(mTmpPos); - if (mTmpPos.equals(mLastSurfacePosition)) { - return; - } - transaction.setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y); - mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y); for (int i = mChildren.size() - 1; i >= 0; i--) { mChildren.get(i).updateSurfacePosition(transaction); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index fcbc80234947f..972dd42f3e15b 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -4482,7 +4482,6 @@ class WindowState extends WindowContainer implements WindowManagerP // Leash is now responsible for position, so set our position to 0. t.setPosition(mSurfaceControl, 0, 0); - mLastSurfacePosition.set(0, 0); } @Override @@ -4498,9 +4497,8 @@ class WindowState extends WindowContainer implements WindowManagerP } transformFrameToSurfacePosition(mFrame.left, mFrame.top, mSurfacePosition); - if (!mSurfaceAnimator.hasLeash() && !mLastSurfacePosition.equals(mSurfacePosition)) { + if (!mSurfaceAnimator.hasLeash()) { t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y); - mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y); } } From 18a527496cc9fe77cd3ea0bfe31d8abc24840236 Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Fri, 5 Jan 2018 09:00:49 -0800 Subject: [PATCH 045/226] Exit split-screen when primary stack moved behind a fullscreen stack. Activities can request their task be moved to the back of the stack, which subsequently causes the stack to move as well. When the stack is a split screen primary stack, this causes an ordering issue where the secondary will not move back while a fullscreen stack has moved in front of the primary. This leads to further ordering issues once the front secondary is dismissed as other stacks are also in the secondary windowing mode. To address this issue, we exit split-screen mode when the primary split-screen stack is moved back. Test: atest ActivityStackTests#testPrimarySplitScreenToFullscreenWhenMovedToBack Change-Id: Ic0597831e046a254b3cba216e1cb2fb11191f2c6 Fixes: 69662547 (cherry picked from commit 8cab4a02bb529f2f1f49fcf2c2c5b5a8531fc033) --- .../com/android/server/am/ActivityStack.java | 8 ++++++ .../android/server/am/ActivityStackTests.java | 25 ++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 10c801da7c03c..e9fa20c63fe0b 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -1013,6 +1013,14 @@ class ActivityStack extends ConfigurationContai return; } + /** + * The intent behind moving a primary split screen stack to the back is usually to hide + * behind the home stack. Exit split screen in this case. + */ + if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + setWindowingMode(WINDOWING_MODE_FULLSCREEN); + } + getDisplay().positionChildAtBottom(this); mStackSupervisor.setFocusStackUnchecked(reason, getDisplay().getTopStack()); if (task != null) { diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java index 6b09363cff0b2..bcbf40e191e69 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java @@ -47,7 +47,7 @@ import org.junit.Test; * Tests for the {@link ActivityStack} class. * * Build/Install/Run: - * bit FrameworksServicesTests:com.android.server.am.ActivityStackTests + * atest ActivityStackTests */ @SmallTest @Presubmit @@ -103,6 +103,29 @@ public class ActivityStackTests extends ActivityTestsBase { assertEquals(mStack.mResumedActivity, r); } + @Test + public void testPrimarySplitScreenToFullscreenWhenMovedToBack() throws Exception { + // Create primary splitscreen stack. This will create secondary stacks and places the + // existing fullscreen stack on the bottom. + final ActivityStack primarySplitScreen = mService.mStackSupervisor.getDefaultDisplay() + .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, + true /* onTop */); + + // Assert windowing mode. + assertEquals(primarySplitScreen.getWindowingMode(), WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + + // Move primary to back. + primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack", + null /* task */); + + // Assert that stack is at the bottom. + assertEquals(mService.mStackSupervisor.getDefaultDisplay().getIndexOf(primarySplitScreen), + 0); + + // Ensure no longer in splitscreen. + assertEquals(primarySplitScreen.getWindowingMode(), WINDOWING_MODE_FULLSCREEN); + } + @Test public void testStopActivityWhenActivityDestroyed() throws Exception { final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build(); From 66e3c7520571234993d2e137f4bb48b97a46ee6e Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Fri, 5 Jan 2018 18:40:14 -0800 Subject: [PATCH 046/226] Dump a few more bits of history about TIME_TICK Bug: 70670726 Test: manual Change-Id: I7d593da8ec236b505bbdba3ece0a638cc41388c2 (cherry picked from commit 12cf0b6921bd4e464f4dbb4a698b078ac3e436fe) --- .../android/server/AlarmManagerService.java | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 472723dfa9093..a024d5a8e3ff0 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -149,6 +149,9 @@ class AlarmManagerService extends SystemService { private long mNextNonWakeup; private long mLastWakeupSet; private long mLastWakeup; + private long mLastTickSet; + private long mLastTickIssued; // elapsed + private long mLastTickReceived; int mBroadcastRefCount = 0; PowerManager.WakeLock mWakeLock; boolean mLastWakeLockUnimportantForLogging; @@ -1596,6 +1599,7 @@ class AlarmManagerService extends SystemService { pw.println(); mForceAppStandbyTracker.dump(pw, " "); + pw.println(); final long nowRTC = System.currentTimeMillis(); final long nowELAPSED = SystemClock.elapsedRealtime(); @@ -1607,8 +1611,12 @@ class AlarmManagerService extends SystemService { pw.println(); pw.print(" mLastTimeChangeClockTime="); pw.print(mLastTimeChangeClockTime); pw.print("="); pw.println(sdf.format(new Date(mLastTimeChangeClockTime))); - pw.print(" mLastTimeChangeRealtime="); - TimeUtils.formatDuration(mLastTimeChangeRealtime, pw); + pw.print(" mLastTimeChangeRealtime="); pw.println(mLastTimeChangeRealtime); + pw.print(" mLastTickIssued="); + TimeUtils.formatDuration(mLastTickIssued - nowELAPSED, pw); + pw.println(); + pw.print(" mLastTickReceived="); pw.println(sdf.format(new Date(mLastTickReceived))); + pw.print(" mLastTickSet="); pw.println(sdf.format(new Date(mLastTickSet))); pw.println(); if (!mInteractive) { pw.print(" Time since non-interactive: "); @@ -3284,6 +3292,9 @@ class AlarmManagerService extends SystemService { if (DEBUG_BATCH) { Slog.v(TAG, "Received TIME_TICK alarm; rescheduling"); } + synchronized (mLock) { + mLastTickReceived = System.currentTimeMillis(); + } scheduleTimeTickEvent(); } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) { // Since the kernel does not keep track of DST, we need to @@ -3309,6 +3320,11 @@ class AlarmManagerService extends SystemService { setImpl(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0, 0, mTimeTickSender, null, null, AlarmManager.FLAG_STANDALONE, workSource, null, Process.myUid(), "android"); + + // Finally, remember when we set the tick alarm + synchronized (mLock) { + mLastTickSet = currentTime; + } } public void scheduleDateChangedEvent() { @@ -3660,6 +3676,11 @@ class AlarmManagerService extends SystemService { if (alarm.operation != null) { // PendingIntent alarm mSendCount++; + + if (alarm.priorityClass.priority == PRIO_TICK) { + mLastTickIssued = nowELAPSED; + } + try { alarm.operation.send(getContext(), 0, mBackgroundIntent.putExtra( From 294224800ddf269ff026c6330b6b9e89454831ac Mon Sep 17 00:00:00 2001 From: chaviw Date: Thu, 4 Jan 2018 17:05:05 -0800 Subject: [PATCH 047/226] Allow stacks in split screen to show on lock screen. The change in ag/3345710 prevented all non full screen stacks from showing on lock screen. Instead allow split screen and full screen stack to show on lock screen. Split screen stacks will become full screen when shown on lock screen. Fixes: 70919978 Test: Put an app into split screen. Lock screen. Receive a phone call. Screen will now properly turn on and show dialer in full screen. Change-Id: I3759cb4b68a7bdad478b5b603b19140965a24b61 (cherry picked from commit 2c50098c118f3210e3b0bf9beb6731220d3ec3f4) --- .../java/com/android/server/am/ActivityDisplay.java | 4 ++-- .../java/com/android/server/am/ActivityRecord.java | 10 ++++++---- .../core/java/com/android/server/am/ActivityStack.java | 6 ++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java index af5cf1ee42028..e38148c7bd421 100644 --- a/services/core/java/com/android/server/am/ActivityDisplay.java +++ b/services/core/java/com/android/server/am/ActivityDisplay.java @@ -556,10 +556,10 @@ class ActivityDisplay extends ConfigurationContainer { return stack == getTopStack(); } - boolean isTopFullscreenStack(ActivityStack stack) { + boolean isTopNotPinnedStack(ActivityStack stack) { for (int i = mStacks.size() - 1; i >= 0; --i) { final ActivityStack current = mStacks.get(i); - if (current.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { + if (!current.inPinnedWindowingMode()) { return current == stack; } } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 8eb5197976412..e1907d37fdf26 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -2730,12 +2730,14 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } /** - * @return true if the activity contains windows that have - * {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set or if the activity has set - * {@link #mShowWhenLocked}. + * @return true if the activity windowing mode is not + * {@link android.app.WindowConfiguration#WINDOWING_MODE_PINNED} and activity contains + * windows that have {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set or if the activity + * has set {@link #mShowWhenLocked}. + * Multi-windowing mode will be exited if true is returned. */ boolean canShowWhenLocked() { - return !inMultiWindowMode() && (mShowWhenLocked + return !inPinnedWindowingMode() && (mShowWhenLocked || service.mWindowManager.containsShowWhenLockedWindow(appToken)); } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index e9fa20c63fe0b..831b31ed89c2d 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -16,7 +16,6 @@ package com.android.server.am; -import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; @@ -144,7 +143,6 @@ import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.BatteryStatsImpl; import com.android.server.Watchdog; import com.android.server.am.ActivityManagerService.ItemMatcher; -import com.android.server.am.EventLogTags; import com.android.server.wm.ConfigurationContainer; import com.android.server.wm.StackWindowController; import com.android.server.wm.StackWindowListener; @@ -1819,7 +1817,7 @@ class ActivityStack extends ConfigurationContai boolean behindFullscreenActivity = !stackShouldBeVisible; boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this) && (isInStackLocked(starting) == null); - final boolean isTopFullscreenStack = getDisplay().isTopFullscreenStack(this); + final boolean isTopNotPinnedStack = getDisplay().isTopNotPinnedStack(this); for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = mTaskHistory.get(taskNdx); final ArrayList activities = task.mActivities; @@ -1841,7 +1839,7 @@ class ActivityStack extends ConfigurationContai // Now check whether it's really visible depending on Keyguard state. final boolean reallyVisible = checkKeyguardVisibility(r, - visibleIgnoringKeyguard, isTop && isTopFullscreenStack); + visibleIgnoringKeyguard, isTop && isTopNotPinnedStack); if (visibleIgnoringKeyguard) { behindFullscreenActivity = updateBehindFullscreen(!stackShouldBeVisible, behindFullscreenActivity, r); From efe9734377e96bcca2d3c16066a35a1dd05f527f Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Mon, 8 Jan 2018 09:49:15 -0800 Subject: [PATCH 048/226] Ignores Resources.NotFoundException when setting ViewStructure.setTestIdEntry(). Test: manual verification using com.facebook.katana Fixes: 71695541 Change-Id: Idefc7d3799f2ef5525a5050d63ca29a19cab730a (cherry picked from commit 01f4f03188588d917f59d1c2737ee7bd27891dda) --- core/java/android/widget/TextView.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 73d53d4dfc8f6..dc54127d85b26 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -311,7 +311,6 @@ import java.util.Locale; public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { static final String LOG_TAG = "TextView"; static final boolean DEBUG_EXTRACT = false; - static final boolean DEBUG_AUTOFILL = false; private static final float[] TEMP_POSITION = new float[2]; // Enum for the "typeface" XML parameter. @@ -9516,7 +9515,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } final AutofillManager afm = mContext.getSystemService(AutofillManager.class); if (afm != null) { - if (DEBUG_AUTOFILL) { + if (android.view.autofill.Helper.sVerbose) { Log.v(LOG_TAG, "sendAfterTextChanged(): notify AFM for text=" + mText); } afm.notifyValueChanged(TextView.this); @@ -10296,7 +10295,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (forAutofill) { structure.setDataIsSensitive(!mSetFromXmlOrResourceId); if (mTextId != ResourceId.ID_NULL) { - structure.setTextIdEntry(getResources().getResourceEntryName(mTextId)); + try { + structure.setTextIdEntry(getResources().getResourceEntryName(mTextId)); + } catch (Resources.NotFoundException e) { + if (android.view.autofill.Helper.sVerbose) { + Log.v(LOG_TAG, "onProvideAutofillStructure(): cannot set name for text id " + + mTextId + ": " + e.getMessage()); + } + } } } From a529279cf39e6b293bfbb5d19e05fcf9b6961e88 Mon Sep 17 00:00:00 2001 From: Rob Carr Date: Tue, 9 Jan 2018 17:09:20 +0000 Subject: [PATCH 049/226] Revert "Superficial improvements to ZOrderingTest." This reverts commit 515a40a14980f0df908a0a08c3220854beee8c14. Reason for revert: CL Earlier in chain causes 71736995 Change-Id: Id1fc0edcad15aa2d785a911a782a05bb6b5766a9 (cherry picked from commit e6282e8f82aa5f8048771f974b158329eb341e45) --- .../com/android/server/wm/ZOrderingTests.java | 145 +++++++++--------- 1 file changed, 74 insertions(+), 71 deletions(-) diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java index bcc9a1cbab7da..34277c3d0f60d 100644 --- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java @@ -76,11 +76,11 @@ public class ZOrderingTests extends WindowTestsBase { return super.setRelativeLayer(sc, relativeTo, layer); } - private int getLayer(SurfaceControl sc) { + int getLayer(SurfaceControl sc) { return mLayersForControl.getOrDefault(sc, 0); } - private SurfaceControl getRelativeLayer(SurfaceControl sc) { + SurfaceControl getRelativeLayer(SurfaceControl sc) { return mRelativeLayersForControl.get(sc); } }; @@ -148,9 +148,8 @@ public class ZOrderingTests extends WindowTestsBase { return p; } - - void assertZOrderGreaterThan(LayerRecordingTransaction t, SurfaceControl left, - SurfaceControl right) throws Exception { + void assertZOrderGreaterThan(LayerRecordingTransaction t, + SurfaceControl left, SurfaceControl right) throws Exception { final LinkedList leftParentChain = getAncestors(t, left); final LinkedList rightParentChain = getAncestors(t, right); @@ -174,12 +173,9 @@ public class ZOrderingTests extends WindowTestsBase { } } - void assertWindowHigher(WindowState left, WindowState right) throws Exception { - assertZOrderGreaterThan(mTransaction, left.getSurfaceControl(), right.getSurfaceControl()); - } - - WindowState createWindow(String name) { - return createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, name); + void assertWindowLayerGreaterThan(LayerRecordingTransaction t, + WindowState left, WindowState right) throws Exception { + assertZOrderGreaterThan(t, left.getSurfaceControl(), right.getSurfaceControl()); } @Test @@ -190,37 +186,38 @@ public class ZOrderingTests extends WindowTestsBase { // The Ime has an higher base layer than app windows and lower base layer than system // windows, so it should be above app windows and below system windows if there isn't an IME // target. - assertWindowHigher(mImeWindow, mChildAppWindowAbove); - assertWindowHigher(mImeWindow, mAppWindow); - assertWindowHigher(mNavBarWindow, mImeWindow); - assertWindowHigher(mStatusBarWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); + assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowHigher(mImeDialogWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception { - final WindowState imeAppTarget = createWindow("imeAppTarget"); + final WindowState imeAppTarget = + createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); sWm.mInputMethodTarget = imeAppTarget; - mDisplayContent.assignChildLayers(mTransaction); // Ime should be above all app windows and below system windows if it is targeting an app // window. - assertWindowHigher(mImeWindow, imeAppTarget); - assertWindowHigher(mImeWindow, mChildAppWindowAbove); - assertWindowHigher(mImeWindow, mAppWindow); - assertWindowHigher(mNavBarWindow, mImeWindow); - assertWindowHigher(mStatusBarWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); + assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowHigher(mImeDialogWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception { - final WindowState imeAppTarget = createWindow("imeAppTarget"); + final WindowState imeAppTarget = + createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget, TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken, "imeAppTargetChildAboveWindow"); @@ -233,38 +230,41 @@ public class ZOrderingTests extends WindowTestsBase { // Ime should be above all app windows except for child windows that are z-ordered above it // and below system windows if it is targeting an app window. - assertWindowHigher(mImeWindow, imeAppTarget); - assertWindowHigher(imeAppTargetChildAboveWindow, mImeWindow); - assertWindowHigher(mImeWindow, mChildAppWindowAbove); - assertWindowHigher(mImeWindow, mAppWindow); - assertWindowHigher(mNavBarWindow, mImeWindow); - assertWindowHigher(mStatusBarWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget); + assertWindowLayerGreaterThan(mTransaction, imeAppTargetChildAboveWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); + assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowHigher(mImeDialogWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception { - final WindowState appBelowImeTarget = createWindow("appBelowImeTarget"); - final WindowState imeAppTarget = createWindow("imeAppTarget"); - final WindowState appAboveImeTarget = createWindow("appAboveImeTarget"); + final WindowState appBelowImeTarget = + createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget"); + final WindowState imeAppTarget = + createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); + final WindowState appAboveImeTarget = + createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget"); sWm.mInputMethodTarget = imeAppTarget; mDisplayContent.assignChildLayers(mTransaction); // Ime should be above all app windows except for non-fullscreen app window above it and // below system windows if it is targeting an app window. - assertWindowHigher(mImeWindow, imeAppTarget); - assertWindowHigher(mImeWindow, appBelowImeTarget); - assertWindowHigher(appAboveImeTarget, mImeWindow); - assertWindowHigher(mImeWindow, mChildAppWindowAbove); - assertWindowHigher(mImeWindow, mAppWindow); - assertWindowHigher(mNavBarWindow, mImeWindow); - assertWindowHigher(mStatusBarWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, appBelowImeTarget); + assertWindowLayerGreaterThan(mTransaction, appAboveImeTarget, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); + assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowHigher(mImeDialogWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); } @Test @@ -278,20 +278,20 @@ public class ZOrderingTests extends WindowTestsBase { // The IME target base layer is higher than all window except for the nav bar window, so the // IME should be above all windows except for the nav bar. - assertWindowHigher(mImeWindow, imeSystemOverlayTarget); - assertWindowHigher(mImeWindow, mChildAppWindowAbove); - assertWindowHigher(mImeWindow, mAppWindow); - assertWindowHigher(mImeWindow, mDockedDividerWindow); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeSystemOverlayTarget); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow); // The IME has a higher base layer than the status bar so we may expect it to go // above the status bar once they are both in the Non-App layer, as past versions of this // test enforced. However this seems like the wrong behavior unless the status bar is the // IME target. - assertWindowHigher(mNavBarWindow, mImeWindow); - assertWindowHigher(mStatusBarWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowHigher(mImeDialogWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); } @Test @@ -299,18 +299,19 @@ public class ZOrderingTests extends WindowTestsBase { sWm.mInputMethodTarget = mStatusBarWindow; mDisplayContent.assignChildLayers(mTransaction); - assertWindowHigher(mImeWindow, mChildAppWindowAbove); - assertWindowHigher(mImeWindow, mAppWindow); - assertWindowHigher(mImeWindow, mDockedDividerWindow); - assertWindowHigher(mImeWindow, mStatusBarWindow); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow); + assertWindowLayerGreaterThan(mTransaction, mImeWindow, mStatusBarWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowHigher(mImeDialogWindow, mImeWindow); + assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); } @Test public void testStackLayers() throws Exception { - final WindowState anyWindow1 = createWindow("anyWindow"); + final WindowState anyWindow1 = + createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "anyWindow"); final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, "pinnedStackWindow"); @@ -320,17 +321,18 @@ public class ZOrderingTests extends WindowTestsBase { final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION, mDisplayContent, "assistantStackWindow"); - final WindowState anyWindow2 = createWindow("anyWindow2"); + final WindowState anyWindow2 = + createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "anyWindow"); mDisplayContent.assignChildLayers(mTransaction); // We compare the split-screen windowing mode to two different normal windowing // mode windows added before and after it to ensure the correct Z ordering irrespective // of ordering in the child list. - assertWindowHigher(dockedStackWindow, anyWindow1); - assertWindowHigher(dockedStackWindow, anyWindow2); - assertWindowHigher(assistantStackWindow, dockedStackWindow); - assertWindowHigher(pinnedStackWindow, assistantStackWindow); + assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, anyWindow1); + assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, anyWindow2); + assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow); + assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow); } @Test @@ -345,9 +347,9 @@ public class ZOrderingTests extends WindowTestsBase { // Ime should be above all app windows and below system windows if it is targeting an app // window. - assertWindowHigher(navBarPanel, mNavBarWindow); - assertWindowHigher(statusBarPanel, mStatusBarWindow); - assertWindowHigher(statusBarSubPanel, statusBarPanel); + assertWindowLayerGreaterThan(mTransaction, navBarPanel, mNavBarWindow); + assertWindowLayerGreaterThan(mTransaction, statusBarPanel, mStatusBarWindow); + assertWindowLayerGreaterThan(mTransaction, statusBarSubPanel, statusBarPanel); } @Test @@ -355,7 +357,8 @@ public class ZOrderingTests extends WindowTestsBase { // TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA // then we can drop all negative layering on the windowing side. - final WindowState anyWindow = createWindow("anyWindow"); + final WindowState anyWindow = + createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "anyWindow"); final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent, "TypeApplicationMediaChild"); final WindowState mediaOverlayChild = createWindow(anyWindow, TYPE_APPLICATION_MEDIA_OVERLAY, @@ -363,8 +366,8 @@ public class ZOrderingTests extends WindowTestsBase { mDisplayContent.assignChildLayers(mTransaction); - assertWindowHigher(anyWindow, mediaOverlayChild); - assertWindowHigher(mediaOverlayChild, child); + assertWindowLayerGreaterThan(mTransaction, anyWindow, mediaOverlayChild); + assertWindowLayerGreaterThan(mTransaction, mediaOverlayChild, child); } @Test @@ -386,9 +389,9 @@ public class ZOrderingTests extends WindowTestsBase { mDisplayContent.assignChildLayers(mTransaction); - assertWindowHigher(dockedDividerWindow, splitScreenWindow); - assertWindowHigher(dockedDividerWindow, splitScreenSecondaryWindow); - assertWindowHigher(assistantStackWindow, dockedDividerWindow); - assertWindowHigher(pinnedStackWindow, dockedDividerWindow); + assertWindowLayerGreaterThan(mTransaction, dockedDividerWindow, splitScreenWindow); + assertWindowLayerGreaterThan(mTransaction, dockedDividerWindow, splitScreenSecondaryWindow); + assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedDividerWindow); + assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, dockedDividerWindow); } } From b9263a104bfe80efdf6d5e4ceb436dd44923509c Mon Sep 17 00:00:00 2001 From: Rob Carr Date: Tue, 9 Jan 2018 17:09:51 +0000 Subject: [PATCH 050/226] Revert "Ensure docked divider is below always-on-top stacks." This reverts commit 11408826496a2c7a8b23e97ec3340972b0df8c36. Reason for revert: CL earlier in chain causes 71736995 Change-Id: Ifd4bbdb5a3c1fbe95c3e9f6b0172c022d43fcdf6 (cherry picked from commit ae3ef5ab3c7f3ea028741392f5dc6b948dc67a8c) --- .../com/android/server/wm/DisplayContent.java | 13 --------- .../com/android/server/wm/ZOrderingTests.java | 27 ------------------- 2 files changed, 40 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 722486351e19d..ccbeb89102895 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -49,7 +49,6 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; -import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; @@ -3571,18 +3570,6 @@ class DisplayContent extends WindowContainer Date: Tue, 9 Jan 2018 17:10:00 +0000 Subject: [PATCH 051/226] Revert "Restore old stack Z ordering promotion." This reverts commit d92e7eb2e9a72ac73e992cf502a080ad47a962f3. Reason for revert: Causes 71736995 Change-Id: I8294bdb65a496bfd0170ec606d20d604d5ef6271 (cherry picked from commit 6693e06ee1a44a70f7dff8ed792823c5153d875a) --- .../com/android/server/wm/DisplayContent.java | 58 ++++++++----------- .../com/android/server/wm/ZOrderingTests.java | 10 +--- 2 files changed, 25 insertions(+), 43 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ccbeb89102895..252eff567cfd7 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3496,47 +3496,37 @@ class DisplayContent extends WindowContainer Date: Wed, 10 Jan 2018 12:31:08 -0800 Subject: [PATCH 052/226] Revert "Revert "Revert "Avoid setting size/position on Transaction if not needed""" This reverts commit 1e8c4c8c0c9111b7ae90e110c73122c7bff1df81. Fixes: 71789344 (cherry picked from commit b38a6704e09b80b9b4fe4f3ad0d16ad6179f612e) Change-Id: Ibfa7408d990e07fa5b299e7edd34c02cfe44be5b --- .../core/java/com/android/server/wm/TaskStack.java | 10 +--------- .../java/com/android/server/wm/WindowContainer.java | 6 ------ .../core/java/com/android/server/wm/WindowState.java | 4 +--- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 7b4281c61f129..bdda944f236bc 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -44,7 +44,6 @@ import static com.android.server.wm.proto.StackProto.WINDOW_CONTAINER; import android.annotation.CallSuper; import android.content.res.Configuration; -import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.os.RemoteException; @@ -146,7 +145,6 @@ public class TaskStack extends WindowContainer implements * For {@link #prepareSurfaces}. */ final Rect mTmpDimBoundsRect = new Rect(); - private final Point mLastSurfaceSize = new Point(); TaskStack(WindowManagerService service, int stackId, StackWindowController controller) { super(service); @@ -746,13 +744,7 @@ public class TaskStack extends WindowContainer implements } final Rect stackBounds = getBounds(); - final int width = stackBounds.width(); - final int height = stackBounds.height(); - if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) { - return; - } - transaction.setSize(mSurfaceControl, width, height); - mLastSurfaceSize.set(width, height); + transaction.setSize(mSurfaceControl, stackBounds.width(), stackBounds.height()); } @Override diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 36e6418a39b52..5548f3d727862 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -95,7 +95,6 @@ class WindowContainer extends ConfigurationContainer< protected final WindowManagerService mService; private final Point mTmpPos = new Point(); - protected final Point mLastSurfacePosition = new Point(); /** Total number of elements in this subtree, including our own hierarchy element. */ private int mTreeWeight = 1; @@ -1179,12 +1178,7 @@ class WindowContainer extends ConfigurationContainer< } getRelativePosition(mTmpPos); - if (mTmpPos.equals(mLastSurfacePosition)) { - return; - } - transaction.setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y); - mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y); for (int i = mChildren.size() - 1; i >= 0; i--) { mChildren.get(i).updateSurfacePosition(transaction); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 0ad60c93bb460..b3809dd8f6c9d 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -4487,7 +4487,6 @@ class WindowState extends WindowContainer implements WindowManagerP // Leash is now responsible for position, so set our position to 0. t.setPosition(mSurfaceControl, 0, 0); - mLastSurfacePosition.set(0, 0); } @Override @@ -4503,9 +4502,8 @@ class WindowState extends WindowContainer implements WindowManagerP } transformFrameToSurfacePosition(mFrame.left, mFrame.top, mSurfacePosition); - if (!mSurfaceAnimator.hasLeash() && !mLastSurfacePosition.equals(mSurfacePosition)) { + if (!mSurfaceAnimator.hasLeash()) { t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y); - mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y); } } From f99d2348c0022641c6786caf6656fee972fd5bcc Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 10 Jan 2018 12:31:21 -0800 Subject: [PATCH 053/226] Revert "Call prepareSurfaces in applySurfacesChangesTransaction" This reverts commit 6b0f8461cfcd6eafa31e5bad0c46048a76a2d616. Fixes: 71789344 (cherry picked from commit ee250367480746dde8275c52810abacaed165c11) Change-Id: Idf78570074fe46255af754525c3adf9408c9abd7 --- .../com/android/server/wm/DisplayContent.java | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 2cc2a0e70ebaa..252eff567cfd7 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -335,6 +335,9 @@ class DisplayContent extends WindowContainer Date: Wed, 10 Jan 2018 16:12:39 -0800 Subject: [PATCH 054/226] Add more WTFs to investigate TIME_TICK issue Bug: 70670726 Test: Manual test Change-Id: Id41431be777c69e553e0d51906ec5b48401f7bd3 (cherry picked from commit 5d93b834bb828a01ef6f51b776e897fca243b487) --- .../com/android/internal/util/ArrayUtils.java | 7 ++ .../android/server/AlarmManagerService.java | 91 ++++++++++++++++++- 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index aa8566885afff..7b023f412cbcf 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -183,6 +183,13 @@ public class ArrayUtils { return array == null ? 0 : array.length; } + /** + * Length of the given collection or 0 if it's null. + */ + public static int size(@Nullable Collection collection) { + return collection == null ? 0 : collection.size(); + } + /** * Checks that value is present as at least one of the elements of the array. * @param array the array to check in diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 86063c3d3bbd4..15c0f3c115451 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -89,10 +89,10 @@ import static android.app.AlarmManager.ELAPSED_REALTIME; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.LocalLog; import com.android.server.ForceAppStandbyTracker.Listener; -import com.android.server.LocalServices; /** * Alarm manager implementaion. @@ -152,6 +152,8 @@ class AlarmManagerService extends SystemService { private long mLastTickSet; private long mLastTickIssued; // elapsed private long mLastTickReceived; + private long mLastTickAdded; + private long mLastTickRemoved; int mBroadcastRefCount = 0; PowerManager.WakeLock mWakeLock; boolean mLastWakeLockUnimportantForLogging; @@ -431,6 +433,9 @@ class AlarmManagerService extends SystemService { end = seed.maxWhenElapsed; flags = seed.flags; alarms.add(seed); + if (seed.operation == mTimeTickSender) { + mLastTickAdded = System.currentTimeMillis(); + } } int size() { @@ -453,6 +458,9 @@ class AlarmManagerService extends SystemService { index = 0 - index - 1; } alarms.add(index, alarm); + if (alarm.operation == mTimeTickSender) { + mLastTickAdded = System.currentTimeMillis(); + } if (DEBUG_BATCH) { Slog.v(TAG, "Adding " + alarm + " to " + this); } @@ -484,6 +492,9 @@ class AlarmManagerService extends SystemService { if (alarm.alarmClock != null) { mNextAlarmClockMayChange = true; } + if (alarm.operation == mTimeTickSender) { + mLastTickRemoved = System.currentTimeMillis(); + } } else { if (alarm.whenElapsed > newStart) { newStart = alarm.whenElapsed; @@ -700,6 +711,39 @@ class AlarmManagerService extends SystemService { } return -1; } + /** @return total count of the alarms in a set of alarm batches. */ + static int getAlarmCount(ArrayList batches) { + int ret = 0; + + final int size = batches.size(); + for (int i = 0; i < size; i++) { + ret += batches.get(i).size(); + } + return ret; + } + + boolean haveAlarmsTimeTickAlarm(ArrayList alarms) { + if (alarms.size() == 0) { + return false; + } + final int batchSize = alarms.size(); + for (int j = 0; j < batchSize; j++) { + if (alarms.get(j).operation == mTimeTickSender) { + return true; + } + } + return false; + } + + boolean haveBatchesTimeTickAlarm(ArrayList batches) { + final int numBatches = batches.size(); + for (int i = 0; i < numBatches; i++) { + if (haveAlarmsTimeTickAlarm(batches.get(i).alarms)) { + return true; + } + } + return false; + } // The RTC clock has moved arbitrarily, so we need to recalculate all the batching void rebatchAllAlarms() { @@ -709,6 +753,11 @@ class AlarmManagerService extends SystemService { } void rebatchAllAlarmsLocked(boolean doValidate) { + final int oldCount = + getAlarmCount(mAlarmBatches) + ArrayUtils.size(mPendingWhileIdleAlarms); + final boolean oldHasTick = haveBatchesTimeTickAlarm(mAlarmBatches) + || haveAlarmsTimeTickAlarm(mPendingWhileIdleAlarms); + ArrayList oldSet = (ArrayList) mAlarmBatches.clone(); mAlarmBatches.clear(); Alarm oldPendingIdleUntil = mPendingIdleUntil; @@ -729,6 +778,18 @@ class AlarmManagerService extends SystemService { restorePendingWhileIdleAlarmsLocked(); } } + final int newCount = + getAlarmCount(mAlarmBatches) + ArrayUtils.size(mPendingWhileIdleAlarms); + final boolean newHasTick = haveBatchesTimeTickAlarm(mAlarmBatches) + || haveAlarmsTimeTickAlarm(mPendingWhileIdleAlarms); + + if (oldCount != newCount) { + Slog.wtf(TAG, "Rebatching: total count changed from " + oldCount + " to " + newCount); + } + if (oldHasTick != newHasTick) { + Slog.wtf(TAG, "Rebatching: hasTick changed from " + oldHasTick + " to " + newHasTick); + } + rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); } @@ -1603,7 +1664,7 @@ class AlarmManagerService extends SystemService { final long nowRTC = System.currentTimeMillis(); final long nowELAPSED = SystemClock.elapsedRealtime(); - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); pw.print(" nowRTC="); pw.print(nowRTC); pw.print("="); pw.print(sdf.format(new Date(nowRTC))); @@ -1613,10 +1674,11 @@ class AlarmManagerService extends SystemService { pw.print("="); pw.println(sdf.format(new Date(mLastTimeChangeClockTime))); pw.print(" mLastTimeChangeRealtime="); pw.println(mLastTimeChangeRealtime); pw.print(" mLastTickIssued="); - TimeUtils.formatDuration(mLastTickIssued - nowELAPSED, pw); - pw.println(); + pw.println(sdf.format(new Date(nowRTC - (nowELAPSED - mLastTickIssued)))); pw.print(" mLastTickReceived="); pw.println(sdf.format(new Date(mLastTickReceived))); pw.print(" mLastTickSet="); pw.println(sdf.format(new Date(mLastTickSet))); + pw.print(" mLastTickAdded="); pw.println(sdf.format(new Date(mLastTickAdded))); + pw.print(" mLastTickRemoved="); pw.println(sdf.format(new Date(mLastTickRemoved))); pw.println(); if (!mInteractive) { pw.print(" Time since non-interactive: "); @@ -2400,6 +2462,10 @@ class AlarmManagerService extends SystemService { } void removeLocked(final int uid) { + if (uid == Process.SYSTEM_UID) { + Slog.wtf(TAG, "removeLocked: Shouldn't for UID=" + uid); + return; + } boolean didRemove = false; final Predicate whichAlarms = (Alarm a) -> a.uid == uid; for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { @@ -2448,6 +2514,7 @@ class AlarmManagerService extends SystemService { boolean didRemove = false; final Predicate whichAlarms = (Alarm a) -> a.matches(packageName); + final boolean oldHasTick = haveBatchesTimeTickAlarm(mAlarmBatches); for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { Batch b = mAlarmBatches.get(i); didRemove |= b.remove(whichAlarms); @@ -2455,6 +2522,11 @@ class AlarmManagerService extends SystemService { mAlarmBatches.remove(i); } } + final boolean newHasTick = haveBatchesTimeTickAlarm(mAlarmBatches); + if (oldHasTick != newHasTick) { + Slog.wtf(TAG, "removeLocked: hasTick changed from " + oldHasTick + " to " + newHasTick); + } + for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) { final Alarm a = mPendingWhileIdleAlarms.get(i); if (a.matches(packageName)) { @@ -2484,6 +2556,10 @@ class AlarmManagerService extends SystemService { } void removeForStoppedLocked(final int uid) { + if (uid == Process.SYSTEM_UID) { + Slog.wtf(TAG, "removeForStoppedLocked: Shouldn't for UID=" + uid); + return; + } boolean didRemove = false; final Predicate whichAlarms = (Alarm a) -> { try { @@ -2524,6 +2600,10 @@ class AlarmManagerService extends SystemService { } void removeUserLocked(int userHandle) { + if (userHandle == UserHandle.USER_SYSTEM) { + Slog.wtf(TAG, "removeForStoppedLocked: Shouldn't for user=" + userHandle); + return; + } boolean didRemove = false; final Predicate whichAlarms = (Alarm a) -> UserHandle.getUserId(a.creatorUid) == userHandle; @@ -3688,6 +3768,9 @@ class AlarmManagerService extends SystemService { mDeliveryTracker, mHandler, null, allowWhileIdle ? mIdleOptions : null); } catch (PendingIntent.CanceledException e) { + if (alarm.operation == mTimeTickSender) { + Slog.wtf(TAG, "mTimeTickSender canceled"); + } if (alarm.repeatInterval > 0) { // This IntentSender is no longer valid, but this // is a repeating alarm, so toss it From 779f150bba2cc59205df9802c568fb228c4a8d4a Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Tue, 16 Jan 2018 11:13:30 -0800 Subject: [PATCH 055/226] Do not pause initializing activities when becoming visible. makeVisibleIfNeeded was recently modified to ensure activities becoming visible were not left in the stopped state. However, the condition was based on simply not being in the paused state, rather than requiring it be in the stopped/stopping state. This can lead to lifecycle issues for moving an initializing activity into the paused state. This changelist ensures that only stopped/stopping activities are considered. Change-Id: I17fc6310db6ee111d1db8f69599090becd1e1760 Fixes: 72028454 Test: atest FrameworksServicesTests:com.android.server.am.ActivityRecordTests (cherry picked from commit 052957b6c0f50d0201f842f9f9291f789814aca2) --- .../core/java/com/android/server/am/ActivityRecord.java | 5 ++--- .../src/com/android/server/am/ActivityRecordTests.java | 9 +++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index b74c8da611a84..b5fbee617fbd8 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -1609,9 +1609,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo mStackSupervisor.mStoppingActivities.remove(this); mStackSupervisor.mGoingToSleepActivities.remove(this); - // If an activity is not in the paused state when becoming visible, cycle to the paused - // state. - if (state != PAUSED) { + // If the activity is stopped or stopping, cycle to the paused state. + if (state == STOPPED || state == STOPPING) { // An activity must be in the {@link PAUSING} state for the system to validate // the move to {@link PAUSED}. state = PAUSING; diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java index b38a4136c94a8..9923fa86758b0 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java @@ -22,6 +22,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING; import static com.android.server.am.ActivityStack.ActivityState.PAUSING; import static com.android.server.am.ActivityStack.ActivityState.STOPPED; import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING; @@ -122,7 +123,15 @@ public class ActivityRecordTests extends ActivityTestsBase { mActivity.makeVisibleIfNeeded(null /* starting */); assertEquals(mActivity.state, PAUSING); + assertTrue(pauseFound.value); + + // Make sure that the state does not change for current non-stopping states. + mActivity.state = INITIALIZING; + + mActivity.makeVisibleIfNeeded(null /* starting */); + + assertEquals(mActivity.state, INITIALIZING); } @Test From 640e86fb1ee3295acabda4074b958c403b97ec41 Mon Sep 17 00:00:00 2001 From: Wyatt Riley Date: Wed, 17 Jan 2018 08:48:27 -0800 Subject: [PATCH 056/226] Dump state of GnssMeasurement & NavMsg requests These can also keep GNSS engine awake, so good to see their status as well. Bug: 71917458 Test: Shows up in adb shell dumpsys location on Pixel 2 Change-Id: I9721b14f10c6f9e6a1242b744867a01f5ea33c67 (cherry picked from commit 74479bd07468caedf9e1e85ab50881858a870867) --- .../com/android/server/location/GnssLocationProvider.java | 4 ++++ .../com/android/server/location/RemoteListenerHelper.java | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index e158819cd89ff..6dc5403acc994 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -2628,6 +2628,10 @@ public class GnssLocationProvider implements LocationProviderInterface { s.append(" mStarted=").append(mStarted).append('\n'); s.append(" mFixInterval=").append(mFixInterval).append('\n'); s.append(" mLowPowerMode=").append(mLowPowerMode).append('\n'); + s.append(" mGnssMeasurementsProvider.isRegistered()=") + .append(mGnssMeasurementsProvider.isRegistered()).append('\n'); + s.append(" mGnssNavigationMessageProvider.isRegistered()=") + .append(mGnssNavigationMessageProvider.isRegistered()).append('\n'); s.append(" mDisableGps (battery saver mode)=").append(mDisableGps).append('\n'); s.append(" mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)); s.append(" ( "); diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java index 58a95162ab82a..fcdb9d1a87cac 100644 --- a/services/core/java/com/android/server/location/RemoteListenerHelper.java +++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java @@ -46,7 +46,8 @@ abstract class RemoteListenerHelper { private final Map mListenerMap = new HashMap<>(); - private boolean mIsRegistered; // must access only on handler thread + private volatile boolean mIsRegistered; // must access only on handler thread, or read-only + private boolean mHasIsSupported; private boolean mIsSupported; @@ -58,6 +59,11 @@ abstract class RemoteListenerHelper { mTag = name; } + // read-only access for a dump() thread assured via volatile + public boolean isRegistered() { + return mIsRegistered; + } + public boolean addListener(@NonNull TListener listener) { Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener."); IBinder binder = listener.asBinder(); From 074879ffcf4e68ada15b30698fcc0be7557adc47 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Tue, 16 Jan 2018 17:45:44 -0800 Subject: [PATCH 057/226] Clear the right uid's alarms, please When we clear data for an app, we need to cancel the alarms for that app's uid -- not the *calling* uid, which could be something like the Settings app's, i.e. the system uid. That's bad. Bug: 72059961 Bug: 70670726 Test: manual Change-Id: Ib6833ce2672bc6dfab6a4be6014a0301611576e2 (cherry picked from commit 5e5c171dea2bb43013fbd72933a1b84df9dba818) --- .../core/java/com/android/server/am/ActivityManagerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7167dcf1c3d7d..bf78ca4319840 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -6242,7 +6242,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Clear its pending alarms AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class); - ami.removeAlarmsForUid(uid); + ami.removeAlarmsForUid(appInfo.uid); } } catch (RemoteException e) { } From 4d298d82aeaac169a08fa532b59ae531c304fb59 Mon Sep 17 00:00:00 2001 From: Ian Pedowitz Date: Thu, 18 Jan 2018 16:24:11 -0800 Subject: [PATCH 058/226] Fixing app compat issue b/72143978 Revert "Remove obsolete workaround." This reverts commit 5e48241a95b843c10f96c57d8544af9d04807218. Bug: 72143978 Bug: 24465209 Test: Tested failing case on sailfish, reverted back all CL's since Test: 3471433 for b/24465209 and apps open (cherry picked from commit 0c3c4d1c7d6fb210548f32a6e6ec01fd9bf90760) Change-Id: Ic7989477ea061f4be3335f0af0ce76ba62820b94 --- native/graphics/jni/Android.bp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp index 61164e068da61..0704e3545b624 100644 --- a/native/graphics/jni/Android.bp +++ b/native/graphics/jni/Android.bp @@ -29,6 +29,13 @@ cc_library_shared { shared_libs: [ "libandroid_runtime", ], + + arch: { + arm: { + // TODO: This is to work around b/24465209. Remove after root cause is fixed + ldflags: ["-Wl,--hash-style=both"], + }, + }, } // The headers module is in frameworks/native/Android.bp. From 383d2d008fc414852a71217772a739a21c28b63c Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Thu, 18 Jan 2018 16:07:36 +0000 Subject: [PATCH 059/226] Check if UserState is null in stopGuestOrEphemeralUserIfBackground - UserState may be removed from mStartedUsers if it has already been stopped Bug: 72133858 Test: Manually create secondary user, and exit the user in SetupWizard Change-Id: I92783f89a9d4de9a7eca81e688b4e115c2f5535a (cherry picked from commit 62995efee31a264e115719bae6827ebb48a3828c) --- services/core/java/com/android/server/am/UserController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index c7210a835767c..65bebc6c235af 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -796,7 +796,7 @@ class UserController implements Handler.Callback { private void stopGuestOrEphemeralUserIfBackground(int oldUserId) { if (DEBUG_MU) Slog.i(TAG, "Stop guest or ephemeral user if background: " + oldUserId); UserState oldUss = mStartedUsers.get(oldUserId); - if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId + if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId || oldUss == null || oldUss.state == UserState.STATE_STOPPING || oldUss.state == UserState.STATE_SHUTDOWN) { return; From 76eaa11672965a9e644270957482916a3be9d4de Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Thu, 18 Jan 2018 21:44:28 +0000 Subject: [PATCH 060/226] Revert "Add setting/experiment for small battery devices to have" This reverts commit 9a60382be1d94e1143cdd16576db774126193798. Reason for revert: Broke battery saver Bug: 72168928 Bug: 69259147 Change-Id: I09664653686a51c297224c863c4fc6d1ff7e5caf Test: Manual test Test: atest CtsBatterySavingTestCases (cherry picked from commit 12391f22dfe4697d55555ba94d0c5c1155dd546e) --- core/java/android/provider/Settings.java | 9 - .../server/forceappstandbytracker.proto | 9 - .../android/provider/SettingsBackupTest.java | 1 - .../server/ForceAppStandbyTracker.java | 164 ++++-------------- .../server/ForceAppStandbyTrackerTest.java | 49 +----- 5 files changed, 35 insertions(+), 197 deletions(-) diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 24e56c0598b23..e380a6a7d4e0b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9851,15 +9851,6 @@ public final class Settings { */ public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled"; - /** - * Whether or not to enable Forced App Standby on small battery devices. - * Type: int (0 for false, 1 for true) - * Default: 0 - * @hide - */ - public static final String FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED - = "forced_app_standby_for_small_battery_enabled"; - /** * Whether or not Network Watchlist feature is enabled. * Type: int (0 for false, 1 for true) diff --git a/core/proto/android/server/forceappstandbytracker.proto b/core/proto/android/server/forceappstandbytracker.proto index c9f7d52ae83fe..8753bf768321b 100644 --- a/core/proto/android/server/forceappstandbytracker.proto +++ b/core/proto/android/server/forceappstandbytracker.proto @@ -41,13 +41,4 @@ message ForceAppStandbyTrackerProto { // Packages that are disallowed OP_RUN_ANY_IN_BACKGROUND. repeated RunAnyInBackgroundRestrictedPackages run_any_in_background_restricted_packages = 5; - - // Whether device is a small battery device - optional bool is_small_battery_device = 6; - - // Whether force app standby for small battery device setting is enabled - optional bool force_all_apps_standby_for_small_battery = 7; - - // Whether device is charging - optional bool is_charging = 8; } diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index d5689c7539a18..c29d79c59dc93 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -213,7 +213,6 @@ public class SettingsBackupTest { Settings.Global.FANCY_IME_ANIMATIONS, Settings.Global.FORCE_ALLOW_ON_EXTERNAL, Settings.Global.FORCED_APP_STANDBY_ENABLED, - Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, Settings.Global.FSTRIM_MANDATORY_INTERVAL, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST, Settings.Global.GLOBAL_HTTP_PROXY_HOST, diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java index 45516115b6298..a75a3675f7f98 100644 --- a/services/core/java/com/android/server/ForceAppStandbyTracker.java +++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java @@ -26,8 +26,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; -import android.net.Uri; -import android.os.BatteryManager; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -91,9 +89,6 @@ public class ForceAppStandbyTracker { private final MyHandler mHandler; - @VisibleForTesting - FeatureFlagsObserver mFlagsObserver; - /** * Pair of (uid (not user-id), packageName) with OP_RUN_ANY_IN_BACKGROUND *not* allowed. */ @@ -119,32 +114,13 @@ public class ForceAppStandbyTracker { boolean mStarted; @GuardedBy("mLock") - boolean mIsCharging; + boolean mForceAllAppsStandby; // True if device is in extreme battery saver mode @GuardedBy("mLock") - boolean mBatterySaverEnabled; + boolean mForcedAppStandbyEnabled; // True if the forced app standby feature is enabled - /** - * True if the forced app standby is currently enabled - */ - @GuardedBy("mLock") - boolean mForceAllAppsStandby; - - /** - * True if the forced app standby for small battery devices feature is enabled in settings - */ - @GuardedBy("mLock") - boolean mForceAllAppStandbyForSmallBattery; - - /** - * True if the forced app standby feature is enabled in settings - */ - @GuardedBy("mLock") - boolean mForcedAppStandbyEnabled; - - @VisibleForTesting - class FeatureFlagsObserver extends ContentObserver { - FeatureFlagsObserver() { + private class FeatureFlagObserver extends ContentObserver { + FeatureFlagObserver() { super(null); } @@ -152,9 +128,6 @@ public class ForceAppStandbyTracker { mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED), false, this); - - mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor( - Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED), false, this); } boolean isForcedAppStandbyEnabled() { @@ -162,43 +135,20 @@ public class ForceAppStandbyTracker { Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1; } - boolean isForcedAppStandbyForSmallBatteryEnabled() { - return Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 0) == 1; - } - @Override - public void onChange(boolean selfChange, Uri uri) { - if (Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED).equals(uri)) { - final boolean enabled = isForcedAppStandbyEnabled(); - synchronized (mLock) { - if (mForcedAppStandbyEnabled == enabled) { - return; - } - mForcedAppStandbyEnabled = enabled; - if (DEBUG) { - Slog.d(TAG, - "Forced app standby feature flag changed: " + mForcedAppStandbyEnabled); - } + public void onChange(boolean selfChange) { + final boolean enabled = isForcedAppStandbyEnabled(); + synchronized (mLock) { + if (mForcedAppStandbyEnabled == enabled) { + return; } - mHandler.notifyForcedAppStandbyFeatureFlagChanged(); - } else if (Settings.Global.getUriFor( - Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED).equals(uri)) { - final boolean enabled = isForcedAppStandbyForSmallBatteryEnabled(); - synchronized (mLock) { - if (mForceAllAppStandbyForSmallBattery == enabled) { - return; - } - mForceAllAppStandbyForSmallBattery = enabled; - if (DEBUG) { - Slog.d(TAG, "Forced app standby for small battery feature flag changed: " - + mForceAllAppStandbyForSmallBattery); - } - updateForceAllAppStandbyState(); + mForcedAppStandbyEnabled = enabled; + if (DEBUG) { + Slog.d(TAG, + "Forced app standby feature flag changed: " + mForcedAppStandbyEnabled); } - } else { - Slog.w(TAG, "Unexpected feature flag uri encountered: " + uri); } + mHandler.notifyFeatureFlagChanged(); } } @@ -339,11 +289,9 @@ public class ForceAppStandbyTracker { mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager()); mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService()); mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal()); - mFlagsObserver = new FeatureFlagsObserver(); - mFlagsObserver.register(); - mForcedAppStandbyEnabled = mFlagsObserver.isForcedAppStandbyEnabled(); - mForceAllAppStandbyForSmallBattery = - mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled(); + final FeatureFlagObserver flagObserver = new FeatureFlagObserver(); + flagObserver.register(); + mForcedAppStandbyEnabled = flagObserver.isForcedAppStandbyEnabled(); try { mIActivityManager.registerUidObserver(new UidObserver(), @@ -358,24 +306,16 @@ public class ForceAppStandbyTracker { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_REMOVED); - filter.addAction(Intent.ACTION_BATTERY_CHANGED); mContext.registerReceiver(new MyReceiver(), filter); refreshForcedAppStandbyUidPackagesLocked(); mPowerManagerInternal.registerLowPowerModeObserver( ServiceType.FORCE_ALL_APPS_STANDBY, - (state) -> { - synchronized (mLock) { - mBatterySaverEnabled = state.batterySaverEnabled; - updateForceAllAppStandbyState(); - } - }); + (state) -> updateForceAllAppsStandby(state.batterySaverEnabled)); - mBatterySaverEnabled = mPowerManagerInternal.getLowPowerState( - ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled; - - updateForceAllAppStandbyState(); + updateForceAllAppsStandby(mPowerManagerInternal.getLowPowerState( + ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled); } } @@ -400,11 +340,6 @@ public class ForceAppStandbyTracker { return LocalServices.getService(PowerManagerInternal.class); } - @VisibleForTesting - boolean isSmallBatteryDevice() { - return ActivityManager.isSmallBatteryDevice(); - } - /** * Update {@link #mRunAnyRestrictedPackages} with the current app ops state. */ @@ -434,29 +369,18 @@ public class ForceAppStandbyTracker { } } - private void updateForceAllAppStandbyState() { - synchronized (mLock) { - if (mIsCharging) { - toggleForceAllAppsStandbyLocked(false); - } else if (mForceAllAppStandbyForSmallBattery - && isSmallBatteryDevice()) { - toggleForceAllAppsStandbyLocked(true); - } else { - toggleForceAllAppsStandbyLocked(mBatterySaverEnabled); - } - } - } - /** * Update {@link #mForceAllAppsStandby} and notifies the listeners. */ - private void toggleForceAllAppsStandbyLocked(boolean enable) { - if (enable == mForceAllAppsStandby) { - return; - } - mForceAllAppsStandby = enable; + void updateForceAllAppsStandby(boolean enable) { + synchronized (mLock) { + if (enable == mForceAllAppsStandby) { + return; + } + mForceAllAppsStandby = enable; - mHandler.notifyForceAllAppsStandbyChanged(); + mHandler.notifyForceAllAppsStandbyChanged(); + } } private int findForcedAppStandbyUidPackageIndexLocked(int uid, @NonNull String packageName) { @@ -591,13 +515,6 @@ public class ForceAppStandbyTracker { if (userId > 0) { mHandler.doUserRemoved(userId); } - } else if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { - int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1); - synchronized (mLock) { - mIsCharging = (status == BatteryManager.BATTERY_STATUS_CHARGING - || status == BatteryManager.BATTERY_STATUS_FULL); - } - updateForceAllAppStandbyState(); } } } @@ -616,7 +533,7 @@ public class ForceAppStandbyTracker { private static final int MSG_TEMP_WHITELIST_CHANGED = 5; private static final int MSG_FORCE_ALL_CHANGED = 6; private static final int MSG_USER_REMOVED = 7; - private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8; + private static final int MSG_FEATURE_FLAG_CHANGED = 8; public MyHandler(Looper looper) { super(looper); @@ -646,8 +563,8 @@ public class ForceAppStandbyTracker { obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget(); } - public void notifyForcedAppStandbyFeatureFlagChanged() { - obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget(); + public void notifyFeatureFlagChanged() { + obtainMessage(MSG_FEATURE_FLAG_CHANGED).sendToTarget(); } public void doUserRemoved(int userId) { @@ -701,7 +618,7 @@ public class ForceAppStandbyTracker { l.onForceAllAppsStandbyChanged(sender); } return; - case MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED: + case MSG_FEATURE_FLAG_CHANGED: // Feature flag for forced app standby changed. final boolean unblockAlarms; synchronized (mLock) { @@ -924,18 +841,6 @@ public class ForceAppStandbyTracker { pw.print("Force all apps standby: "); pw.println(isForceAllAppsStandbyEnabled()); - pw.print(indent); - pw.print("Small Battery Device: "); - pw.println(isSmallBatteryDevice()); - - pw.print(indent); - pw.print("Force all apps standby for small battery device: "); - pw.println(mForceAllAppStandbyForSmallBattery); - - pw.print(indent); - pw.print("Charging: "); - pw.println(mIsCharging); - pw.print(indent); pw.print("Foreground uids: ["); @@ -975,11 +880,6 @@ public class ForceAppStandbyTracker { final long token = proto.start(fieldId); proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY, mForceAllAppsStandby); - proto.write(ForceAppStandbyTrackerProto.IS_SMALL_BATTERY_DEVICE, - isSmallBatteryDevice()); - proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY_FOR_SMALL_BATTERY, - mForceAllAppStandbyForSmallBattery); - proto.write(ForceAppStandbyTrackerProto.IS_CHARGING, mIsCharging); for (int i = 0; i < mForegroundUids.size(); i++) { if (mForegroundUids.valueAt(i)) { diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java index 6a21931e04182..66d0da13fff1c 100644 --- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java @@ -42,7 +42,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.os.BatteryManager; import android.os.Handler; import android.os.Looper; import android.os.PowerManager.ServiceType; @@ -51,17 +50,13 @@ import android.os.PowerSaveState; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; -import android.provider.Settings; -import android.provider.Settings.Global; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import android.test.mock.MockContentResolver; import android.util.ArraySet; import android.util.Pair; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; -import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.ForceAppStandbyTracker.Listener; import org.junit.Before; @@ -107,9 +102,6 @@ public class ForceAppStandbyTrackerTest { PowerManagerInternal injectPowerManagerInternal() { return mMockPowerManagerInternal; } - - @Override - boolean isSmallBatteryDevice() { return mIsSmallBatteryDevice; }; } private static final int UID_1 = Process.FIRST_APPLICATION_UID + 1; @@ -145,11 +137,7 @@ public class ForceAppStandbyTrackerTest { private Consumer mPowerSaveObserver; private BroadcastReceiver mReceiver; - private MockContentResolver mMockContentResolver; - private FakeSettingsProvider mFakeSettingsProvider; - private boolean mPowerSaveMode; - private boolean mIsSmallBatteryDevice; private final ArraySet> mRestrictedPackages = new ArraySet(); @@ -186,17 +174,13 @@ public class ForceAppStandbyTrackerTest { } private void callStart(ForceAppStandbyTrackerTestable instance) throws RemoteException { + // Set up functions that start() calls. when(mMockPowerManagerInternal.getLowPowerState(eq(ServiceType.FORCE_ALL_APPS_STANDBY))) .thenAnswer(inv -> getPowerSaveState()); when(mMockAppOpsManager.getPackagesForOps( any(int[].class) - )).thenAnswer(inv -> new ArrayList()); - - mMockContentResolver = new MockContentResolver(); - mFakeSettingsProvider = new FakeSettingsProvider(); - when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver); - mMockContentResolver.addProvider(Settings.AUTHORITY, mFakeSettingsProvider); + )).thenAnswer(inv -> new ArrayList()); // Call start. instance.start(); @@ -224,6 +208,7 @@ public class ForceAppStandbyTrackerTest { verify(mMockPowerManagerInternal).registerLowPowerModeObserver( eq(ServiceType.FORCE_ALL_APPS_STANDBY), powerSaveObserverCaptor.capture()); + verify(mMockContext).registerReceiver( receiverCaptor.capture(), any(IntentFilter.class)); @@ -236,7 +221,6 @@ public class ForceAppStandbyTrackerTest { assertNotNull(mAppOpsCallback); assertNotNull(mPowerSaveObserver); assertNotNull(mReceiver); - assertNotNull(instance.mFlagsObserver); } private void setAppOps(int uid, String packageName, boolean restrict) throws RemoteException { @@ -838,33 +822,6 @@ public class ForceAppStandbyTrackerTest { assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2)); } - @Test - public void testSmallBatteryAndCharging() throws Exception { - // This is a small battery device - mIsSmallBatteryDevice = true; - - final ForceAppStandbyTrackerTestable instance = newInstance(); - callStart(instance); - assertFalse(instance.isForceAllAppsStandbyEnabled()); - - // Setting/experiment for all app standby for small battery is enabled - Global.putInt(mMockContentResolver, Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 1); - instance.mFlagsObserver.onChange(true, - Global.getUriFor(Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED)); - assertTrue(instance.isForceAllAppsStandbyEnabled()); - - // When battery is charging, force app standby is disabled - Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); - intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING); - mReceiver.onReceive(mMockContext, intent); - assertFalse(instance.isForceAllAppsStandbyEnabled()); - - // When battery stops charging, force app standby is enabled - intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_DISCHARGING); - mReceiver.onReceive(mMockContext, intent); - assertTrue(instance.isForceAllAppsStandbyEnabled()); - } - static int[] array(int... appIds) { Arrays.sort(appIds); return appIds; From 9c25085863a2762b06e9e967cffe2d8e78b17526 Mon Sep 17 00:00:00 2001 From: Wyatt Riley Date: Wed, 17 Jan 2018 08:48:27 -0800 Subject: [PATCH 061/226] GnssMeasurementsListener - Fix indexing, add & remove Safer indexing open requests by IBinder instead of IInterface Covers GnssNavigationMessage as well. Also dumping currently open GnssMeasurement & NavMsg requests Bug: 71917458 Test: Able to repro issue consistently before, fixed after logs look as expected as well, on Pixel 2 Change-Id: I2e4722a23355295a5f597541f31a3b77534cadd7 (cherry picked from commit 11cc74913d7c5c06ae12ea232a53918c0959fc86) --- .../server/LocationManagerService.java | 41 ++++++++++++------- .../server/location/GnssLocationProvider.java | 4 ++ .../server/location/RemoteListenerHelper.java | 8 +++- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index ea748db159e1f..6c63f43234ab8 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -232,10 +232,9 @@ public class LocationManagerService extends ILocationManager.Stub { private final ArraySet mBackgroundThrottlePackageWhitelist = new ArraySet<>(); - private final ArrayMap mGnssMeasurementsListeners = - new ArrayMap<>(); + private final ArrayMap mGnssMeasurementsListeners = new ArrayMap<>(); - private final ArrayMap + private final ArrayMap mGnssNavigationMessageListeners = new ArrayMap<>(); // current active user on the device - other users are denied location data @@ -438,23 +437,23 @@ public class LocationManagerService extends ILocationManager.Stub { applyRequirementsLocked(provider); } - for (Entry entry - : mGnssMeasurementsListeners.entrySet()) { + for (Entry entry : mGnssMeasurementsListeners.entrySet()) { if (entry.getValue().mUid == uid) { if (D) { Log.d(TAG, "gnss measurements listener from uid " + uid + " is now " + (foreground ? "foreground" : "background)")); } if (foreground || isThrottlingExemptLocked(entry.getValue())) { - mGnssMeasurementsProvider.addListener(entry.getKey()); + mGnssMeasurementsProvider.addListener( + IGnssMeasurementsListener.Stub.asInterface(entry.getKey())); } else { - mGnssMeasurementsProvider.removeListener(entry.getKey()); + mGnssMeasurementsProvider.removeListener( + IGnssMeasurementsListener.Stub.asInterface(entry.getKey())); } } } - for (Entry entry - : mGnssNavigationMessageListeners.entrySet()) { + for (Entry entry : mGnssNavigationMessageListeners.entrySet()) { if (entry.getValue().mUid == uid) { if (D) { Log.d(TAG, "gnss navigation message listener from uid " @@ -462,9 +461,11 @@ public class LocationManagerService extends ILocationManager.Stub { + (foreground ? "foreground" : "background)")); } if (foreground || isThrottlingExemptLocked(entry.getValue())) { - mGnssNavigationMessageProvider.addListener(entry.getKey()); + mGnssNavigationMessageProvider.addListener( + IGnssNavigationMessageListener.Stub.asInterface(entry.getKey())); } else { - mGnssNavigationMessageProvider.removeListener(entry.getKey()); + mGnssNavigationMessageProvider.removeListener( + IGnssNavigationMessageListener.Stub.asInterface(entry.getKey())); } } } @@ -2401,7 +2402,7 @@ public class LocationManagerService extends ILocationManager.Stub { synchronized (mLock) { Identity callerIdentity = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName); - mGnssMeasurementsListeners.put(listener, callerIdentity); + mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity); long identity = Binder.clearCallingIdentity(); try { if (isThrottlingExemptLocked(callerIdentity) @@ -2421,7 +2422,7 @@ public class LocationManagerService extends ILocationManager.Stub { public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) { if (mGnssMeasurementsProvider != null) { synchronized (mLock) { - mGnssMeasurementsListeners.remove(listener); + mGnssMeasurementsListeners.remove(listener.asBinder()); mGnssMeasurementsProvider.removeListener(listener); } } @@ -2438,7 +2439,7 @@ public class LocationManagerService extends ILocationManager.Stub { synchronized (mLock) { Identity callerIdentity = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName); - mGnssNavigationMessageListeners.put(listener, callerIdentity); + mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity); long identity = Binder.clearCallingIdentity(); try { if (isThrottlingExemptLocked(callerIdentity) @@ -2458,7 +2459,7 @@ public class LocationManagerService extends ILocationManager.Stub { public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) { if (mGnssNavigationMessageProvider != null) { synchronized (mLock) { - mGnssNavigationMessageListeners.remove(listener); + mGnssNavigationMessageListeners.remove(listener.asBinder()); mGnssNavigationMessageProvider.removeListener(listener); } } @@ -3180,6 +3181,16 @@ public class LocationManagerService extends ILocationManager.Stub { pw.println(" " + record); } } + pw.println(" Active GnssMeasurement Listeners:"); + for (Identity identity : mGnssMeasurementsListeners.values()) { + pw.println(" " + identity.mPid + " " + identity.mUid + " " + + identity.mPackageName + ": " + isThrottlingExemptLocked(identity)); + } + pw.println(" Active GnssNavigationMessage Listeners:"); + for (Identity identity : mGnssNavigationMessageListeners.values()) { + pw.println(" " + identity.mPid + " " + identity.mUid + " " + + identity.mPackageName + ": " + isThrottlingExemptLocked(identity)); + } pw.println(" Overlay Provider Packages:"); for (LocationProviderInterface provider : mProviders) { if (provider instanceof LocationProviderProxy) { diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index e158819cd89ff..6dc5403acc994 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -2628,6 +2628,10 @@ public class GnssLocationProvider implements LocationProviderInterface { s.append(" mStarted=").append(mStarted).append('\n'); s.append(" mFixInterval=").append(mFixInterval).append('\n'); s.append(" mLowPowerMode=").append(mLowPowerMode).append('\n'); + s.append(" mGnssMeasurementsProvider.isRegistered()=") + .append(mGnssMeasurementsProvider.isRegistered()).append('\n'); + s.append(" mGnssNavigationMessageProvider.isRegistered()=") + .append(mGnssNavigationMessageProvider.isRegistered()).append('\n'); s.append(" mDisableGps (battery saver mode)=").append(mDisableGps).append('\n'); s.append(" mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)); s.append(" ( "); diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java index 58a95162ab82a..fcdb9d1a87cac 100644 --- a/services/core/java/com/android/server/location/RemoteListenerHelper.java +++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java @@ -46,7 +46,8 @@ abstract class RemoteListenerHelper { private final Map mListenerMap = new HashMap<>(); - private boolean mIsRegistered; // must access only on handler thread + private volatile boolean mIsRegistered; // must access only on handler thread, or read-only + private boolean mHasIsSupported; private boolean mIsSupported; @@ -58,6 +59,11 @@ abstract class RemoteListenerHelper { mTag = name; } + // read-only access for a dump() thread assured via volatile + public boolean isRegistered() { + return mIsRegistered; + } + public boolean addListener(@NonNull TListener listener) { Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener."); IBinder binder = listener.asBinder(); From 892de1ded97ecaab7f43f6ed1ed5029fbeaf4557 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Thu, 18 Jan 2018 18:43:35 +0000 Subject: [PATCH 062/226] Add back missing synchronized in stopGuestOrEphemeralUserIfBackground - mStartedUsers and mCurrentUserId should be locked by mLock Bug: 72133858 Test: Manually create secondary user, and exit the user in SetupWizard Change-Id: If59749c06c5d8174462a6f2a255517c60321d9f4 (cherry picked from commit fbc7717851f2681d15fedd684ab530d7a4fc842e) --- .../java/com/android/server/am/UserController.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 65bebc6c235af..1a47aa5cd7773 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -795,11 +795,13 @@ class UserController implements Handler.Callback { */ private void stopGuestOrEphemeralUserIfBackground(int oldUserId) { if (DEBUG_MU) Slog.i(TAG, "Stop guest or ephemeral user if background: " + oldUserId); - UserState oldUss = mStartedUsers.get(oldUserId); - if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId || oldUss == null - || oldUss.state == UserState.STATE_STOPPING - || oldUss.state == UserState.STATE_SHUTDOWN) { - return; + synchronized(mLock) { + UserState oldUss = mStartedUsers.get(oldUserId); + if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId || oldUss == null + || oldUss.state == UserState.STATE_STOPPING + || oldUss.state == UserState.STATE_SHUTDOWN) { + return; + } } UserInfo userInfo = getUserInfo(oldUserId); From 269d28d3d0b88370360e982f1277829485371c65 Mon Sep 17 00:00:00 2001 From: Yao Chen Date: Fri, 19 Jan 2018 00:12:18 +0000 Subject: [PATCH 063/226] Revert "Enable init.rc for statsd to start statsd by default in boot time" This reverts commit 56967528d7bcc7a1342d68f19e0ffa79f2a1ed59. Change-Id: I4b76bf5783f0df43603e82168009f33296d1f840 (cherry picked from commit 5f3dc878dc0a0fddd4f7b3bc80e1277ffde22c51) --- cmds/statsd/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 2c35d413631be..b803a985f3c8a 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -134,7 +134,7 @@ LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \ LOCAL_MODULE_CLASS := EXECUTABLES -LOCAL_INIT_RC := statsd.rc +#LOCAL_INIT_RC := statsd.rc include $(BUILD_EXECUTABLE) From 08976bf0cc565d159f86c586dea94ede1c3e4c41 Mon Sep 17 00:00:00 2001 From: Artem Iglikov Date: Sun, 21 Jan 2018 13:04:41 +0000 Subject: [PATCH 064/226] Revert "Add accessibility support for tooltips" This reverts commit 4365cef6ddca2503524853a62655e226636831de. Reason for revert: Cannot access views by resource-id via uiautomator Bug: 72271943 Change-Id: I5e07a8c5775aa79df0c240b2133daaf62f6d460b (cherry picked from commit b43225adf306d512da6e39ddc4e3e4dd43ff1f8f) --- api/current.txt | 6 -- core/java/android/view/View.java | 60 +++++++------------ .../accessibility/AccessibilityNodeInfo.java | 55 ----------------- core/res/res/values/ids.xml | 5 -- core/res/res/values/public.xml | 2 - 5 files changed, 21 insertions(+), 107 deletions(-) diff --git a/api/current.txt b/api/current.txt index 97acea931b5c6..276c300695816 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1809,7 +1809,6 @@ package android { public static final class R.id { ctor public R.id(); field public static final int accessibilityActionContextClick = 16908348; // 0x102003c - field public static final int accessibilityActionHideTooltip = 16908357; // 0x1020045 field public static final int accessibilityActionMoveWindow = 16908354; // 0x1020042 field public static final int accessibilityActionScrollDown = 16908346; // 0x102003a field public static final int accessibilityActionScrollLeft = 16908345; // 0x1020039 @@ -1818,7 +1817,6 @@ package android { field public static final int accessibilityActionScrollUp = 16908344; // 0x1020038 field public static final int accessibilityActionSetProgress = 16908349; // 0x102003d field public static final int accessibilityActionShowOnScreen = 16908342; // 0x1020036 - field public static final int accessibilityActionShowTooltip = 16908356; // 0x1020044 field public static final int addToDictionary = 16908330; // 0x102002a field public static final int autofill = 16908355; // 0x1020043 field public static final int background = 16908288; // 0x1020000 @@ -48217,7 +48215,6 @@ package android.view.accessibility { method public java.lang.CharSequence getText(); method public int getTextSelectionEnd(); method public int getTextSelectionStart(); - method public java.lang.CharSequence getTooltipText(); method public android.view.accessibility.AccessibilityNodeInfo getTraversalAfter(); method public android.view.accessibility.AccessibilityNodeInfo getTraversalBefore(); method public java.lang.String getViewIdResourceName(); @@ -48305,7 +48302,6 @@ package android.view.accessibility { method public void setSource(android.view.View, int); method public void setText(java.lang.CharSequence); method public void setTextSelection(int, int); - method public void setTooltipText(java.lang.CharSequence); method public void setTraversalAfter(android.view.View); method public void setTraversalAfter(android.view.View, int); method public void setTraversalBefore(android.view.View); @@ -48375,7 +48371,6 @@ package android.view.accessibility { field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_DISMISS; field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_EXPAND; field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_FOCUS; - field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_HIDE_TOOLTIP; field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_LONG_CLICK; field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_MOVE_WINDOW; field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_NEXT_AT_MOVEMENT_GRANULARITY; @@ -48395,7 +48390,6 @@ package android.view.accessibility { field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_SELECTION; field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_TEXT; field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SHOW_ON_SCREEN; - field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SHOW_TOOLTIP; } public static final class AccessibilityNodeInfo.CollectionInfo { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 47bdf9df47192..05770c3575262 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -16,8 +16,6 @@ package android.view; -import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED; - import static java.lang.Math.max; import android.animation.AnimatorInflater; @@ -8550,12 +8548,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, info.setLongClickable(isLongClickable()); info.setContextClickable(isContextClickable()); info.setLiveRegion(getAccessibilityLiveRegion()); - if ((mTooltipInfo != null) && (mTooltipInfo.mTooltipText != null)) { - info.setTooltipText(mTooltipInfo.mTooltipText); - info.addAction((mTooltipInfo.mTooltipPopup == null) - ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_TOOLTIP - : AccessibilityNodeInfo.AccessibilityAction.ACTION_HIDE_TOOLTIP); - } // TODO: These make sense only if we are in an AdapterView but all // views can be selected. Maybe from accessibility perspective @@ -8959,7 +8951,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return; } mAccessibilityTraversalBeforeId = beforeId; - notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED); + notifyAccessibilityStateChanged( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } /** @@ -9002,7 +8995,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return; } mAccessibilityTraversalAfterId = afterId; - notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED); + notifyAccessibilityStateChanged( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } /** @@ -9044,7 +9038,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, && mID == View.NO_ID) { mID = generateViewId(); } - notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED); + notifyAccessibilityStateChanged( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } /** @@ -10544,7 +10539,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (pflags3 != mPrivateFlags3) { mPrivateFlags3 = pflags3; - notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED); + notifyAccessibilityStateChanged(AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } @@ -11372,7 +11367,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK; mPrivateFlags2 |= (mode << PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT) & PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK; - notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED); + notifyAccessibilityStateChanged( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } @@ -11431,7 +11427,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (!maySkipNotify || oldIncludeForAccessibility != includeForAccessibility()) { notifyAccessibilitySubtreeChanged(); } else { - notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED); + notifyAccessibilityStateChanged( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } } @@ -11841,7 +11838,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, || getAccessibilitySelectionEnd() != end) && (start == end)) { setAccessibilitySelection(start, end); - notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED); + notifyAccessibilityStateChanged( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); return true; } } break; @@ -11858,21 +11856,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return true; } } break; - case R.id.accessibilityActionShowTooltip: { - if ((mTooltipInfo != null) && (mTooltipInfo.mTooltipPopup != null)) { - // Tooltip already showing - return false; - } - return showLongClickTooltip(0, 0); - } - case R.id.accessibilityActionHideTooltip: { - if ((mTooltipInfo == null) || (mTooltipInfo.mTooltipPopup == null)) { - // No tooltip showing - return false; - } - hideTooltip(); - return true; - } } return false; } @@ -13906,10 +13889,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (oldIncludeForAccessibility != includeForAccessibility()) { notifyAccessibilitySubtreeChanged(); } else { - notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED); + notifyAccessibilityStateChanged( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } else if ((changed & ENABLED_MASK) != 0) { - notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED); + notifyAccessibilityStateChanged( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } } @@ -21869,7 +21854,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (selected) { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); } else { - notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED); + notifyAccessibilityStateChanged( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } } @@ -27048,8 +27034,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final boolean fromTouch = (mPrivateFlags3 & PFLAG3_FINGER_DOWN) == PFLAG3_FINGER_DOWN; mTooltipInfo.mTooltipPopup.show(this, x, y, fromTouch, mTooltipInfo.mTooltipText); mAttachInfo.mTooltipHost = this; - // The available accessibility actions have changed - notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED); return true; } @@ -27068,8 +27052,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mAttachInfo != null) { mAttachInfo.mTooltipHost = null; } - // The available accessibility actions have changed - notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED); } private boolean showLongClickTooltip(int x, int y) { @@ -27078,8 +27060,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return showTooltip(x, y, true); } - private boolean showHoverTooltip() { - return showTooltip(mTooltipInfo.mAnchorX, mTooltipInfo.mAnchorY, false); + private void showHoverTooltip() { + showTooltip(mTooltipInfo.mAnchorX, mTooltipInfo.mAnchorY, false); } boolean dispatchTooltipHoverEvent(MotionEvent event) { diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 909032a0c2795..3ee282ec3ac2f 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -727,7 +727,6 @@ public class AccessibilityNodeInfo implements Parcelable { private CharSequence mError; private CharSequence mPaneTitle; private CharSequence mContentDescription; - private CharSequence mTooltipText; private String mViewIdResourceName; private ArrayList mExtraDataKeys; @@ -2655,34 +2654,6 @@ public class AccessibilityNodeInfo implements Parcelable { : contentDescription.subSequence(0, contentDescription.length()); } - /** - * Gets the tooltip text of this node. - * - * @return The tooltip text. - */ - @Nullable - public CharSequence getTooltipText() { - return mTooltipText; - } - - /** - * Sets the tooltip text of this node. - *

- * Note: Cannot be called from an - * {@link android.accessibilityservice.AccessibilityService}. - * This class is made immutable before being delivered to an AccessibilityService. - *

- * - * @param tooltipText The tooltip text. - * - * @throws IllegalStateException If called from an AccessibilityService. - */ - public void setTooltipText(@Nullable CharSequence tooltipText) { - enforceNotSealed(); - mTooltipText = (tooltipText == null) ? null - : tooltipText.subSequence(0, tooltipText.length()); - } - /** * Sets the view for which the view represented by this info serves as a * label for accessibility purposes. @@ -3237,9 +3208,6 @@ public class AccessibilityNodeInfo implements Parcelable { if (!Objects.equals(mPaneTitle, DEFAULT.mPaneTitle)) { nonDefaultFields |= bitAt(fieldIndex); } - if (!Objects.equals(mTooltipText, DEFAULT.mTooltipText)) { - nonDefaultFields |= bitAt(fieldIndex); - } fieldIndex++; if (!Objects.equals(mViewIdResourceName, DEFAULT.mViewIdResourceName)) { nonDefaultFields |= bitAt(fieldIndex); @@ -3361,8 +3329,6 @@ public class AccessibilityNodeInfo implements Parcelable { parcel.writeCharSequence(mContentDescription); } if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mPaneTitle); - if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mTooltipText); - if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeString(mViewIdResourceName); if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mTextSelectionStart); @@ -3435,7 +3401,6 @@ public class AccessibilityNodeInfo implements Parcelable { mError = other.mError; mContentDescription = other.mContentDescription; mPaneTitle = other.mPaneTitle; - mTooltipText = other.mTooltipText; mViewIdResourceName = other.mViewIdResourceName; if (mActions != null) mActions.clear(); @@ -3557,7 +3522,6 @@ public class AccessibilityNodeInfo implements Parcelable { mContentDescription = parcel.readCharSequence(); } if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readString(); - if (isBitSet(nonDefaultFields, fieldIndex++)) mTooltipText = parcel.readCharSequence(); if (isBitSet(nonDefaultFields, fieldIndex++)) mViewIdResourceName = parcel.readString(); if (isBitSet(nonDefaultFields, fieldIndex++)) mTextSelectionStart = parcel.readInt(); @@ -3720,10 +3684,6 @@ public class AccessibilityNodeInfo implements Parcelable { return "ACTION_SET_PROGRESS"; case R.id.accessibilityActionContextClick: return "ACTION_CONTEXT_CLICK"; - case R.id.accessibilityActionShowTooltip: - return "ACTION_SHOW_TOOLTIP"; - case R.id.accessibilityActionHideTooltip: - return "ACTION_HIDE_TOOLTIP"; default: return "ACTION_UNKNOWN"; } @@ -3837,7 +3797,6 @@ public class AccessibilityNodeInfo implements Parcelable { builder.append("; error: ").append(mError); builder.append("; maxTextLength: ").append(mMaxTextLength); builder.append("; contentDescription: ").append(mContentDescription); - builder.append("; tooltipText: ").append(mTooltipText); builder.append("; viewIdResName: ").append(mViewIdResourceName); builder.append("; checkable: ").append(isCheckable()); @@ -4252,20 +4211,6 @@ public class AccessibilityNodeInfo implements Parcelable { public static final AccessibilityAction ACTION_MOVE_WINDOW = new AccessibilityAction(R.id.accessibilityActionMoveWindow); - /** - * Action to show a tooltip. A node should expose this action only for views with tooltip - * text that but are not currently showing a tooltip. - */ - public static final AccessibilityAction ACTION_SHOW_TOOLTIP = - new AccessibilityAction(R.id.accessibilityActionShowTooltip); - - /** - * Action to hide a tooltip. A node should expose this action only for views that are - * currently showing a tooltip. - */ - public static final AccessibilityAction ACTION_HIDE_TOOLTIP = - new AccessibilityAction(R.id.accessibilityActionHideTooltip); - private final int mActionId; private final CharSequence mLabel; diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index c90a0df5d7b1d..b40117e9e40ba 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -163,9 +163,4 @@ - - - - - diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 80fc5db5d435e..9cdf5531225b3 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2874,8 +2874,6 @@ - - From ab078c0dcf060e39c57b5c31d3ddb35319627ebf Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Mon, 22 Jan 2018 20:01:21 +0100 Subject: [PATCH 065/226] Fix NPE when creating remote animations Test: go/wm-smoke Bug: 72302602 Change-Id: Ifbae370ea559a9fda5ca442ceef0f6db8229466b (cherry picked from commit 1777021fcf989c0a1d2b7aa533a7b464ad85a807) --- .../server/wm/RemoteAnimationController.java | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index 688b4ffcee4b8..8515dcb699707 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -99,11 +99,15 @@ class RemoteAnimationController { } private RemoteAnimationTarget[] createAnimations() { - final RemoteAnimationTarget[] result = new RemoteAnimationTarget[mPendingAnimations.size()]; + final ArrayList targets = new ArrayList<>(); for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { - result[i] = mPendingAnimations.get(i).createRemoteAppAnimation(); + final RemoteAnimationTarget target = + mPendingAnimations.get(i).createRemoteAppAnimation(); + if (target != null) { + targets.add(target); + } } - return result; + return targets.toArray(new RemoteAnimationTarget[targets.size()]); } private void onAnimationFinished() { @@ -145,9 +149,17 @@ class RemoteAnimationController { } RemoteAnimationTarget createRemoteAppAnimation() { - return new RemoteAnimationTarget(mAppWindowToken.getTask().mTaskId, getMode(), + final Task task = mAppWindowToken.getTask(); + final WindowState mainWindow = mAppWindowToken.findMainWindow(); + if (task == null) { + return null; + } + if (mainWindow == null) { + return null; + } + return new RemoteAnimationTarget(task.mTaskId, getMode(), mCapturedLeash, !mAppWindowToken.fillsParent(), - mAppWindowToken.findMainWindow().mWinAnimator.mLastClipRect, + mainWindow.mWinAnimator.mLastClipRect, mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds); } From 8a7f03d06956bce87a94604f88e967d5628ce254 Mon Sep 17 00:00:00 2001 From: Leif Wilden Date: Mon, 22 Jan 2018 20:21:33 +0000 Subject: [PATCH 066/226] Revert "Move instrumentation classes to SettingsLib to share between mobile/TV." This reverts commit 58dccbe1e23c7ecb77c5716fe31d5c9d549124ff. Reason for revert: Broke fingerprint setup flow. b/72267201 Change-Id: I9901d0ec077ff0a5b50f59a9f1b1c6156f6f09bd (cherry picked from commit 2e9781034f796826727a55ad36f8213a8d7452fb) --- .../core/instrumentation/EventLogWriter.java | 110 -------- .../core/instrumentation/Instrumentable.java | 28 -- .../core/instrumentation/LogWriter.java | 84 ------ .../MetricsFeatureProvider.java | 159 ----------- .../SharedPreferencesLogger.java | 259 ------------------ .../VisibilityLoggerMixin.java | 96 ------- .../MetricsFeatureProviderTest.java | 132 --------- .../SharedPreferenceLoggerTest.java | 181 ------------ .../VisibilityLoggerMixinTest.java | 122 --------- 9 files changed, 1171 deletions(-) delete mode 100644 packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java delete mode 100644 packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java delete mode 100644 packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java delete mode 100644 packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java delete mode 100644 packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java delete mode 100644 packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java delete mode 100644 packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java delete mode 100644 packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java delete mode 100644 packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java deleted file mode 100644 index 72273046ef29b..0000000000000 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settingslib.core.instrumentation; - -import android.content.Context; -import android.metrics.LogMaker; -import android.util.Log; -import android.util.Pair; - -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto; - -/** - * {@link LogWriter} that writes data to eventlog. - */ -public class EventLogWriter implements LogWriter { - - private final MetricsLogger mMetricsLogger = new MetricsLogger(); - - public void visible(Context context, int source, int category) { - final LogMaker logMaker = new LogMaker(category) - .setType(MetricsProto.MetricsEvent.TYPE_OPEN) - .addTaggedData(MetricsProto.MetricsEvent.FIELD_CONTEXT, source); - MetricsLogger.action(logMaker); - } - - public void hidden(Context context, int category) { - MetricsLogger.hidden(context, category); - } - - public void action(int category, int value, Pair... taggedData) { - if (taggedData == null || taggedData.length == 0) { - mMetricsLogger.action(category, value); - } else { - final LogMaker logMaker = new LogMaker(category) - .setType(MetricsProto.MetricsEvent.TYPE_ACTION) - .setSubtype(value); - for (Pair pair : taggedData) { - logMaker.addTaggedData(pair.first, pair.second); - } - mMetricsLogger.write(logMaker); - } - } - - public void action(int category, boolean value, Pair... taggedData) { - action(category, value ? 1 : 0, taggedData); - } - - public void action(Context context, int category, Pair... taggedData) { - action(context, category, "", taggedData); - } - - public void actionWithSource(Context context, int source, int category) { - final LogMaker logMaker = new LogMaker(category) - .setType(MetricsProto.MetricsEvent.TYPE_ACTION); - if (source != MetricsProto.MetricsEvent.VIEW_UNKNOWN) { - logMaker.addTaggedData(MetricsProto.MetricsEvent.FIELD_CONTEXT, source); - } - MetricsLogger.action(logMaker); - } - - /** @deprecated use {@link #action(int, int, Pair[])} */ - @Deprecated - public void action(Context context, int category, int value) { - MetricsLogger.action(context, category, value); - } - - /** @deprecated use {@link #action(int, boolean, Pair[])} */ - @Deprecated - public void action(Context context, int category, boolean value) { - MetricsLogger.action(context, category, value); - } - - public void action(Context context, int category, String pkg, - Pair... taggedData) { - if (taggedData == null || taggedData.length == 0) { - MetricsLogger.action(context, category, pkg); - } else { - final LogMaker logMaker = new LogMaker(category) - .setType(MetricsProto.MetricsEvent.TYPE_ACTION) - .setPackageName(pkg); - for (Pair pair : taggedData) { - logMaker.addTaggedData(pair.first, pair.second); - } - MetricsLogger.action(logMaker); - } - } - - public void count(Context context, String name, int value) { - MetricsLogger.count(context, name, value); - } - - public void histogram(Context context, String name, int bucket) { - MetricsLogger.histogram(context, name, bucket); - } -} diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java deleted file mode 100644 index dbc61c26e82e7..0000000000000 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settingslib.core.instrumentation; - -public interface Instrumentable { - - int METRICS_CATEGORY_UNKNOWN = 0; - - /** - * Instrumented name for a view as defined in - * {@link com.android.internal.logging.nano.MetricsProto.MetricsEvent}. - */ - int getMetricsCategory(); -} diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java deleted file mode 100644 index 4b9f5727208da..0000000000000 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.settingslib.core.instrumentation; - -import android.content.Context; -import android.util.Pair; - -/** - * Generic log writer interface. - */ -public interface LogWriter { - - /** - * Logs a visibility event when view becomes visible. - */ - void visible(Context context, int source, int category); - - /** - * Logs a visibility event when view becomes hidden. - */ - void hidden(Context context, int category); - - /** - * Logs a user action. - */ - void action(int category, int value, Pair... taggedData); - - /** - * Logs a user action. - */ - void action(int category, boolean value, Pair... taggedData); - - /** - * Logs an user action. - */ - void action(Context context, int category, Pair... taggedData); - - /** - * Logs an user action. - */ - void actionWithSource(Context context, int source, int category); - - /** - * Logs an user action. - * @deprecated use {@link #action(int, int, Pair[])} - */ - @Deprecated - void action(Context context, int category, int value); - - /** - * Logs an user action. - * @deprecated use {@link #action(int, boolean, Pair[])} - */ - @Deprecated - void action(Context context, int category, boolean value); - - /** - * Logs an user action. - */ - void action(Context context, int category, String pkg, Pair... taggedData); - - /** - * Logs a count. - */ - void count(Context context, String name, int value); - - /** - * Logs a histogram event. - */ - void histogram(Context context, String name, int bucket); -} diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java deleted file mode 100644 index 1e5b378e931c4..0000000000000 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.settingslib.core.instrumentation; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.text.TextUtils; -import android.util.Pair; - -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; - -import java.util.ArrayList; -import java.util.List; - -/** - * FeatureProvider for metrics. - */ -public class MetricsFeatureProvider { - private List mLoggerWriters; - - public MetricsFeatureProvider() { - mLoggerWriters = new ArrayList<>(); - installLogWriters(); - } - - protected void installLogWriters() { - mLoggerWriters.add(new EventLogWriter()); - } - - public void visible(Context context, int source, int category) { - for (LogWriter writer : mLoggerWriters) { - writer.visible(context, source, category); - } - } - - public void hidden(Context context, int category) { - for (LogWriter writer : mLoggerWriters) { - writer.hidden(context, category); - } - } - - public void actionWithSource(Context context, int source, int category) { - for (LogWriter writer : mLoggerWriters) { - writer.actionWithSource(context, source, category); - } - } - - /** - * Logs a user action. Includes the elapsed time since the containing - * fragment has been visible. - */ - public void action(VisibilityLoggerMixin visibilityLogger, int category, int value) { - for (LogWriter writer : mLoggerWriters) { - writer.action(category, value, - sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible())); - } - } - - /** - * Logs a user action. Includes the elapsed time since the containing - * fragment has been visible. - */ - public void action(VisibilityLoggerMixin visibilityLogger, int category, boolean value) { - for (LogWriter writer : mLoggerWriters) { - writer.action(category, value, - sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible())); - } - } - - public void action(Context context, int category, Pair... taggedData) { - for (LogWriter writer : mLoggerWriters) { - writer.action(context, category, taggedData); - } - } - - /** @deprecated use {@link #action(VisibilityLoggerMixin, int, int)} */ - @Deprecated - public void action(Context context, int category, int value) { - for (LogWriter writer : mLoggerWriters) { - writer.action(context, category, value); - } - } - - /** @deprecated use {@link #action(VisibilityLoggerMixin, int, boolean)} */ - @Deprecated - public void action(Context context, int category, boolean value) { - for (LogWriter writer : mLoggerWriters) { - writer.action(context, category, value); - } - } - - public void action(Context context, int category, String pkg, - Pair... taggedData) { - for (LogWriter writer : mLoggerWriters) { - writer.action(context, category, pkg, taggedData); - } - } - - public void count(Context context, String name, int value) { - for (LogWriter writer : mLoggerWriters) { - writer.count(context, name, value); - } - } - - public void histogram(Context context, String name, int bucket) { - for (LogWriter writer : mLoggerWriters) { - writer.histogram(context, name, bucket); - } - } - - public int getMetricsCategory(Object object) { - if (object == null || !(object instanceof Instrumentable)) { - return MetricsEvent.VIEW_UNKNOWN; - } - return ((Instrumentable) object).getMetricsCategory(); - } - - public void logDashboardStartIntent(Context context, Intent intent, - int sourceMetricsCategory) { - if (intent == null) { - return; - } - final ComponentName cn = intent.getComponent(); - if (cn == null) { - final String action = intent.getAction(); - if (TextUtils.isEmpty(action)) { - // Not loggable - return; - } - action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, action, - Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory)); - return; - } else if (TextUtils.equals(cn.getPackageName(), context.getPackageName())) { - // Going to a Setting internal page, skip click logging in favor of page's own - // visibility logging. - return; - } - action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, cn.flattenToString(), - Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory)); - } - - private Pair sinceVisibleTaggedData(long timestamp) { - return Pair.create(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, timestamp); - } -} diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java deleted file mode 100644 index facce4e0bcbb0..0000000000000 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package com.android.settingslib.core.instrumentation; - -import android.annotation.Nullable; -import android.content.ComponentName; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.os.AsyncTask; -import android.support.annotation.VisibleForTesting; -import android.text.TextUtils; -import android.util.Log; -import android.util.Pair; - -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; - -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentSkipListSet; - -public class SharedPreferencesLogger implements SharedPreferences { - - private static final String LOG_TAG = "SharedPreferencesLogger"; - - private final String mTag; - private final Context mContext; - private final MetricsFeatureProvider mMetricsFeature; - private final Set mPreferenceKeySet; - - public SharedPreferencesLogger(Context context, String tag, - MetricsFeatureProvider metricsFeature) { - mContext = context; - mTag = tag; - mMetricsFeature = metricsFeature; - mPreferenceKeySet = new ConcurrentSkipListSet<>(); - } - - @Override - public Map getAll() { - return null; - } - - @Override - public String getString(String key, @Nullable String defValue) { - return defValue; - } - - @Override - public Set getStringSet(String key, @Nullable Set defValues) { - return defValues; - } - - @Override - public int getInt(String key, int defValue) { - return defValue; - } - - @Override - public long getLong(String key, long defValue) { - return defValue; - } - - @Override - public float getFloat(String key, float defValue) { - return defValue; - } - - @Override - public boolean getBoolean(String key, boolean defValue) { - return defValue; - } - - @Override - public boolean contains(String key) { - return false; - } - - @Override - public Editor edit() { - return new EditorLogger(); - } - - @Override - public void registerOnSharedPreferenceChangeListener( - OnSharedPreferenceChangeListener listener) { - } - - @Override - public void unregisterOnSharedPreferenceChangeListener( - OnSharedPreferenceChangeListener listener) { - } - - private void logValue(String key, Object value) { - logValue(key, value, false /* forceLog */); - } - - private void logValue(String key, Object value, boolean forceLog) { - final String prefKey = buildPrefKey(mTag, key); - if (!forceLog && !mPreferenceKeySet.contains(prefKey)) { - // Pref key doesn't exist in set, this is initial display so we skip metrics but - // keeps track of this key. - mPreferenceKeySet.add(prefKey); - return; - } - // TODO: Remove count logging to save some resource. - mMetricsFeature.count(mContext, buildCountName(prefKey, value), 1); - - final Pair valueData; - if (value instanceof Long) { - final Long longVal = (Long) value; - final int intVal; - if (longVal > Integer.MAX_VALUE) { - intVal = Integer.MAX_VALUE; - } else if (longVal < Integer.MIN_VALUE) { - intVal = Integer.MIN_VALUE; - } else { - intVal = longVal.intValue(); - } - valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, - intVal); - } else if (value instanceof Integer) { - valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, - value); - } else if (value instanceof Boolean) { - valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, - (Boolean) value ? 1 : 0); - } else if (value instanceof Float) { - valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE, - value); - } else if (value instanceof String) { - Log.d(LOG_TAG, "Tried to log string preference " + prefKey + " = " + value); - valueData = null; - } else { - Log.w(LOG_TAG, "Tried to log unloggable object" + value); - valueData = null; - } - if (valueData != null) { - // Pref key exists in set, log it's change in metrics. - mMetricsFeature.action(mContext, MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE, - Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, prefKey), - valueData); - } - } - - @VisibleForTesting - void logPackageName(String key, String value) { - final String prefKey = mTag + "/" + key; - mMetricsFeature.action(mContext, MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE, value, - Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, prefKey)); - } - - private void safeLogValue(String key, String value) { - new AsyncPackageCheck().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, key, value); - } - - public static String buildCountName(String prefKey, Object value) { - return prefKey + "|" + value; - } - - public static String buildPrefKey(String tag, String key) { - return tag + "/" + key; - } - - private class AsyncPackageCheck extends AsyncTask { - @Override - protected Void doInBackground(String... params) { - String key = params[0]; - String value = params[1]; - PackageManager pm = mContext.getPackageManager(); - try { - // Check if this might be a component. - ComponentName name = ComponentName.unflattenFromString(value); - if (value != null) { - value = name.getPackageName(); - } - } catch (Exception e) { - } - try { - pm.getPackageInfo(value, PackageManager.MATCH_ANY_USER); - logPackageName(key, value); - } catch (PackageManager.NameNotFoundException e) { - // Clearly not a package, and it's unlikely this preference is in prefSet, so - // lets force log it. - logValue(key, value, true /* forceLog */); - } - return null; - } - } - - public class EditorLogger implements Editor { - @Override - public Editor putString(String key, @Nullable String value) { - safeLogValue(key, value); - return this; - } - - @Override - public Editor putStringSet(String key, @Nullable Set values) { - safeLogValue(key, TextUtils.join(",", values)); - return this; - } - - @Override - public Editor putInt(String key, int value) { - logValue(key, value); - return this; - } - - @Override - public Editor putLong(String key, long value) { - logValue(key, value); - return this; - } - - @Override - public Editor putFloat(String key, float value) { - logValue(key, value); - return this; - } - - @Override - public Editor putBoolean(String key, boolean value) { - logValue(key, value); - return this; - } - - @Override - public Editor remove(String key) { - return this; - } - - @Override - public Editor clear() { - return this; - } - - @Override - public boolean commit() { - return true; - } - - @Override - public void apply() { - } - } -} diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java deleted file mode 100644 index 79838962ef1ee..0000000000000 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settingslib.core.instrumentation; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; - -import android.os.SystemClock; -import com.android.internal.logging.nano.MetricsProto; -import com.android.settingslib.core.lifecycle.LifecycleObserver; -import com.android.settingslib.core.lifecycle.events.OnPause; -import com.android.settingslib.core.lifecycle.events.OnResume; - -import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN; - -/** - * Logs visibility change of a fragment. - */ -public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPause { - - private static final String TAG = "VisibilityLoggerMixin"; - - private final int mMetricsCategory; - - private MetricsFeatureProvider mMetricsFeature; - private int mSourceMetricsCategory = MetricsProto.MetricsEvent.VIEW_UNKNOWN; - private long mVisibleTimestamp; - - /** - * The metrics category constant for logging source when a setting fragment is opened. - */ - public static final String EXTRA_SOURCE_METRICS_CATEGORY = ":settings:source_metrics"; - - private VisibilityLoggerMixin() { - mMetricsCategory = METRICS_CATEGORY_UNKNOWN; - } - - public VisibilityLoggerMixin(int metricsCategory, MetricsFeatureProvider metricsFeature) { - mMetricsCategory = metricsCategory; - mMetricsFeature = metricsFeature; - } - - @Override - public void onResume() { - mVisibleTimestamp = SystemClock.elapsedRealtime(); - if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) { - mMetricsFeature.visible(null /* context */, mSourceMetricsCategory, mMetricsCategory); - } - } - - @Override - public void onPause() { - mVisibleTimestamp = 0; - if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) { - mMetricsFeature.hidden(null /* context */, mMetricsCategory); - } - } - - /** - * Sets source metrics category for this logger. Source is the caller that opened this UI. - */ - public void setSourceMetricsCategory(Activity activity) { - if (mSourceMetricsCategory != MetricsProto.MetricsEvent.VIEW_UNKNOWN || activity == null) { - return; - } - final Intent intent = activity.getIntent(); - if (intent == null) { - return; - } - mSourceMetricsCategory = intent.getIntExtra(EXTRA_SOURCE_METRICS_CATEGORY, - MetricsProto.MetricsEvent.VIEW_UNKNOWN); - } - - /** Returns elapsed time since onResume() */ - public long elapsedTimeSinceVisible() { - if (mVisibleTimestamp == 0) { - return 0; - } - return SystemClock.elapsedRealtime() - mVisibleTimestamp; - } -} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java deleted file mode 100644 index 8bea51d1696dd..0000000000000 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.settingslib.core.instrumentation; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.util.Pair; - -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.settingslib.TestConfig; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; -import org.robolectric.util.ReflectionHelpers; - -import java.util.ArrayList; -import java.util.List; - -@RunWith(SettingsLibRobolectricTestRunner.class) -@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) -public class MetricsFeatureProviderTest { - private static int CATEGORY = 10; - private static boolean SUBTYPE_BOOLEAN = true; - private static int SUBTYPE_INTEGER = 1; - private static long ELAPSED_TIME = 1000; - - @Mock private LogWriter mockLogWriter; - @Mock private VisibilityLoggerMixin mockVisibilityLogger; - - private Context mContext; - private MetricsFeatureProvider mProvider; - - @Captor - private ArgumentCaptor mPairCaptor; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; - mProvider = new MetricsFeatureProvider(); - List writers = new ArrayList<>(); - writers.add(mockLogWriter); - ReflectionHelpers.setField(mProvider, "mLoggerWriters", writers); - - when(mockVisibilityLogger.elapsedTimeSinceVisible()).thenReturn(ELAPSED_TIME); - } - - @Test - public void logDashboardStartIntent_intentEmpty_shouldNotLog() { - mProvider.logDashboardStartIntent(mContext, null /* intent */, - MetricsEvent.SETTINGS_GESTURES); - - verifyNoMoreInteractions(mockLogWriter); - } - - @Test - public void logDashboardStartIntent_intentHasNoComponent_shouldLog() { - final Intent intent = new Intent(Intent.ACTION_ASSIST); - - mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES); - - verify(mockLogWriter).action( - eq(mContext), - eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK), - anyString(), - eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES))); - } - - @Test - public void logDashboardStartIntent_intentIsExternal_shouldLog() { - final Intent intent = new Intent().setComponent(new ComponentName("pkg", "cls")); - - mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES); - - verify(mockLogWriter).action( - eq(mContext), - eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK), - anyString(), - eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES))); - } - - @Test - public void action_BooleanLogsElapsedTime() { - mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_BOOLEAN); - verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_BOOLEAN), mPairCaptor.capture()); - - Pair value = mPairCaptor.getValue(); - assertThat(value.first instanceof Integer).isTrue(); - assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS); - assertThat(value.second).isEqualTo(ELAPSED_TIME); - } - - @Test - public void action_IntegerLogsElapsedTime() { - mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_INTEGER); - verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_INTEGER), mPairCaptor.capture()); - - Pair value = mPairCaptor.getValue(); - assertThat(value.first instanceof Integer).isTrue(); - assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS); - assertThat(value.second).isEqualTo(ELAPSED_TIME); - } -} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java deleted file mode 100644 index d558a645aeb7e..0000000000000 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.settingslib.core.instrumentation; - -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE; -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE; -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE; -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.content.Context; -import android.content.SharedPreferences; -import android.util.Pair; - -import com.android.settingslib.TestConfig; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - -import com.google.common.truth.Platform; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Answers; -import org.mockito.ArgumentMatcher; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.annotation.Config; - -@RunWith(SettingsLibRobolectricTestRunner.class) -@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) -public class SharedPreferenceLoggerTest { - - private static final String TEST_TAG = "tag"; - private static final String TEST_KEY = "key"; - - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private Context mContext; - - private ArgumentMatcher> mNamePairMatcher; - @Mock - private MetricsFeatureProvider mMetricsFeature; - private SharedPreferencesLogger mSharedPrefLogger; - - @Before - public void init() { - MockitoAnnotations.initMocks(this); - mSharedPrefLogger = new SharedPreferencesLogger(mContext, TEST_TAG, mMetricsFeature); - mNamePairMatcher = pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, String.class); - } - - @Test - public void putInt_shouldNotLogInitialPut() { - final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); - editor.putInt(TEST_KEY, 1); - editor.putInt(TEST_KEY, 1); - editor.putInt(TEST_KEY, 1); - editor.putInt(TEST_KEY, 2); - editor.putInt(TEST_KEY, 2); - editor.putInt(TEST_KEY, 2); - editor.putInt(TEST_KEY, 2); - - verify(mMetricsFeature, times(6)).action(any(Context.class), anyInt(), - argThat(mNamePairMatcher), - argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.class))); - } - - @Test - public void putBoolean_shouldNotLogInitialPut() { - final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); - editor.putBoolean(TEST_KEY, true); - editor.putBoolean(TEST_KEY, true); - editor.putBoolean(TEST_KEY, false); - editor.putBoolean(TEST_KEY, false); - editor.putBoolean(TEST_KEY, false); - - - verify(mMetricsFeature).action(any(Context.class), anyInt(), - argThat(mNamePairMatcher), - argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, true))); - verify(mMetricsFeature, times(3)).action(any(Context.class), anyInt(), - argThat(mNamePairMatcher), - argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, false))); - } - - @Test - public void putLong_shouldNotLogInitialPut() { - final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); - editor.putLong(TEST_KEY, 1); - editor.putLong(TEST_KEY, 1); - editor.putLong(TEST_KEY, 1); - editor.putLong(TEST_KEY, 1); - editor.putLong(TEST_KEY, 2); - - verify(mMetricsFeature, times(4)).action(any(Context.class), anyInt(), - argThat(mNamePairMatcher), - argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.class))); - } - - @Test - public void putLong_biggerThanIntMax_shouldLogIntMax() { - final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); - final long veryBigNumber = 500L + Integer.MAX_VALUE; - editor.putLong(TEST_KEY, 1); - editor.putLong(TEST_KEY, veryBigNumber); - - verify(mMetricsFeature).action(any(Context.class), anyInt(), - argThat(mNamePairMatcher), - argThat(pairMatches( - FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.MAX_VALUE))); - } - - @Test - public void putLong_smallerThanIntMin_shouldLogIntMin() { - final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); - final long veryNegativeNumber = -500L + Integer.MIN_VALUE; - editor.putLong(TEST_KEY, 1); - editor.putLong(TEST_KEY, veryNegativeNumber); - - verify(mMetricsFeature).action(any(Context.class), anyInt(), - argThat(mNamePairMatcher), - argThat(pairMatches( - FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.MIN_VALUE))); - } - - @Test - public void putFloat_shouldNotLogInitialPut() { - final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); - editor.putFloat(TEST_KEY, 1); - editor.putFloat(TEST_KEY, 1); - editor.putFloat(TEST_KEY, 1); - editor.putFloat(TEST_KEY, 1); - editor.putFloat(TEST_KEY, 2); - - verify(mMetricsFeature, times(4)).action(any(Context.class), anyInt(), - argThat(mNamePairMatcher), - argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE, Float.class))); - } - - @Test - public void logPackage_shouldUseLogPackageApi() { - mSharedPrefLogger.logPackageName("key", "com.android.settings"); - verify(mMetricsFeature).action(any(Context.class), - eq(ACTION_SETTINGS_PREFERENCE_CHANGE), - eq("com.android.settings"), - any(Pair.class)); - } - - private ArgumentMatcher> pairMatches(int tag, Class clazz) { - return pair -> pair.first == tag && Platform.isInstanceOfType(pair.second, clazz); - } - - private ArgumentMatcher> pairMatches(int tag, boolean bool) { - return pair -> pair.first == tag - && Platform.isInstanceOfType(pair.second, Integer.class) - && pair.second.equals((bool ? 1 : 0)); - } - - private ArgumentMatcher> pairMatches(int tag, int val) { - return pair -> pair.first == tag - && Platform.isInstanceOfType(pair.second, Integer.class) - && pair.second.equals(val); - } -} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java deleted file mode 100644 index a2648861d1d85..0000000000000 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.settingslib.core.instrumentation; - -import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN; - -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; - -import com.android.internal.logging.nano.MetricsProto; -import com.android.settingslib.SettingsLibRobolectricTestRunner; -import com.android.settingslib.TestConfig; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.annotation.Config; - - -@RunWith(SettingsLibRobolectricTestRunner.class) -@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) -public class VisibilityLoggerMixinTest { - - @Mock - private MetricsFeatureProvider mMetricsFeature; - - private VisibilityLoggerMixin mMixin; - - @Before - public void init() { - MockitoAnnotations.initMocks(this); - mMixin = new VisibilityLoggerMixin(TestInstrumentable.TEST_METRIC, mMetricsFeature); - } - - @Test - public void shouldLogVisibleOnResume() { - mMixin.onResume(); - - verify(mMetricsFeature, times(1)) - .visible(nullable(Context.class), eq(MetricsProto.MetricsEvent.VIEW_UNKNOWN), - eq(TestInstrumentable.TEST_METRIC)); - } - - @Test - public void shouldLogVisibleWithSource() { - final Intent sourceIntent = new Intent() - .putExtra(VisibilityLoggerMixin.EXTRA_SOURCE_METRICS_CATEGORY, - MetricsProto.MetricsEvent.SETTINGS_GESTURES); - final Activity activity = mock(Activity.class); - when(activity.getIntent()).thenReturn(sourceIntent); - mMixin.setSourceMetricsCategory(activity); - mMixin.onResume(); - - verify(mMetricsFeature, times(1)) - .visible(nullable(Context.class), eq(MetricsProto.MetricsEvent.SETTINGS_GESTURES), - eq(TestInstrumentable.TEST_METRIC)); - } - - @Test - public void shouldLogHideOnPause() { - mMixin.onPause(); - - verify(mMetricsFeature, times(1)) - .hidden(nullable(Context.class), eq(TestInstrumentable.TEST_METRIC)); - } - - @Test - public void shouldNotLogIfMetricsFeatureIsNull() { - mMixin = new VisibilityLoggerMixin(TestInstrumentable.TEST_METRIC, null); - mMixin.onResume(); - mMixin.onPause(); - - verify(mMetricsFeature, never()) - .hidden(nullable(Context.class), anyInt()); - } - - @Test - public void shouldNotLogIfMetricsCategoryIsUnknown() { - mMixin = new VisibilityLoggerMixin(METRICS_CATEGORY_UNKNOWN, mMetricsFeature); - - mMixin.onResume(); - mMixin.onPause(); - - verify(mMetricsFeature, never()) - .hidden(nullable(Context.class), anyInt()); - } - - private final class TestInstrumentable implements Instrumentable { - - public static final int TEST_METRIC = 12345; - - @Override - public int getMetricsCategory() { - return TEST_METRIC; - } - } -} From 775f9fed3d15bc50ff1c4aa8974c0ff52c77da1b Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Tue, 23 Jan 2018 03:16:16 -0800 Subject: [PATCH 067/226] Revert "libandroidfw: Improve performance of AssetManager2" This reverts commit 392132748416719e3df427e6ac8dc11af194342c. (cherry picked from commit 50706b6ebc224920bceffa66baa30734de5e27ff) Change-Id: Iab1273ed7bc147a3dda63c59b5a3664674193473 --- libs/androidfw/Android.bp | 1 - libs/androidfw/AssetManager2.cpp | 271 ++++-------------- libs/androidfw/LoadedArsc.cpp | 259 +++++++++++------ .../include/androidfw/AssetManager2.h | 66 ++--- libs/androidfw/include/androidfw/LoadedArsc.h | 101 +++---- libs/androidfw/tests/ApkAssets_test.cpp | 78 ++--- libs/androidfw/tests/LoadedArsc_test.cpp | 165 +++++------ libs/androidfw/tests/TestHelpers.h | 1 - 8 files changed, 413 insertions(+), 529 deletions(-) diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 70d52164ff748..7c9078b164a2f 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -145,7 +145,6 @@ cc_test { "tests/TypeWrappers_test.cpp", "tests/ZipUtils_test.cpp", ], - static_libs: ["libgmock"], target: { android: { srcs: [ diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index a558ff7ccfc12..2fc8e952707b1 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -36,31 +36,6 @@ namespace android { -struct FindEntryResult { - // A pointer to the resource table entry for this resource. - // If the size of the entry is > sizeof(ResTable_entry), it can be cast to - // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry; - - // The configuration for which the resulting entry was defined. This is already swapped to host - // endianness. - ResTable_config config; - - // The bitmask of configuration axis with which the resource value varies. - uint32_t type_flags; - - // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table; - - // The string pool reference to the type's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef type_string_ref; - - // The string pool reference to the entry's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef entry_string_ref; -}; - AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } @@ -69,7 +44,6 @@ bool AssetManager2::SetApkAssets(const std::vector& apk_assets bool invalidate_caches) { apk_assets_ = apk_assets; BuildDynamicRefTable(); - RebuildFilterList(); if (invalidate_caches) { InvalidateCaches(static_cast(-1)); } @@ -105,7 +79,7 @@ void AssetManager2::BuildDynamicRefTable() { PackageGroup* package_group = &package_groups_[idx]; // Add the package and to the set of packages with the same ID. - package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); + package_group->packages_.push_back(package.get()); package_group->cookies_.push_back(static_cast(i)); // Add the package name -> build time ID mappings. @@ -120,7 +94,7 @@ void AssetManager2::BuildDynamicRefTable() { // Now assign the runtime IDs so that we have a build-time to runtime ID map. const auto package_groups_end = package_groups_.end(); for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) { - const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName(); + const std::string& package_name = iter->packages_[0]->GetPackageName(); for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) { iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()), iter->dynamic_ref_table.mAssignedPackageId); @@ -134,20 +108,17 @@ void AssetManager2::DumpToLog() const { std::string list; for (size_t i = 0; i < package_ids_.size(); i++) { if (package_ids_[i] != 0xff) { - base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]); + base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]); } } LOG(INFO) << "Package ID map: " << list; - for (const auto& package_group : package_groups_) { - list = ""; - for (const auto& package : package_group.packages_) { - base::StringAppendF(&list, "%s(%02x), ", package.loaded_package_->GetPackageName().c_str(), - package.loaded_package_->GetPackageId()); - } - LOG(INFO) << base::StringPrintf("PG (%02x): ", - package_group.dynamic_ref_table.mAssignedPackageId) - << list; + for (const auto& package_group: package_groups_) { + list = ""; + for (const auto& package : package_group.packages_) { + base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId()); + } + LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list; } } @@ -186,54 +157,52 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { configuration_ = configuration; if (diff) { - RebuildFilterList(); InvalidateCaches(static_cast(diff)); } } std::set AssetManager2::GetResourceConfigurations(bool exclude_system, - bool exclude_mipmap) const { + bool exclude_mipmap) { ATRACE_CALL(); std::set configurations; for (const PackageGroup& package_group : package_groups_) { - for (const ConfiguredPackage& package : package_group.packages_) { - if (exclude_system && package.loaded_package_->IsSystem()) { + for (const LoadedPackage* package : package_group.packages_) { + if (exclude_system && package->IsSystem()) { continue; } - package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); + package->CollectConfigurations(exclude_mipmap, &configurations); } } return configurations; } std::set AssetManager2::GetResourceLocales(bool exclude_system, - bool merge_equivalent_languages) const { + bool merge_equivalent_languages) { ATRACE_CALL(); std::set locales; for (const PackageGroup& package_group : package_groups_) { - for (const ConfiguredPackage& package : package_group.packages_) { - if (exclude_system && package.loaded_package_->IsSystem()) { + for (const LoadedPackage* package : package_group.packages_) { + if (exclude_system && package->IsSystem()) { continue; } - package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales); + package->CollectLocales(merge_equivalent_languages, &locales); } } return locales; } -std::unique_ptr AssetManager2::Open(const std::string& filename, - Asset::AccessMode mode) const { +std::unique_ptr AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, mode); } std::unique_ptr AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) const { + Asset::AccessMode mode) { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, cookie, mode); } -std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) const { +std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) { ATRACE_CALL(); std::string full_path = "assets/" + dirname; @@ -267,7 +236,7 @@ std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) con // is inconsistent for split APKs. std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie) const { + ApkAssetsCookie* out_cookie) { ATRACE_CALL(); for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) { std::unique_ptr asset = apk_assets_[i]->Open(filename, mode); @@ -286,8 +255,7 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, - ApkAssetsCookie cookie, - Asset::AccessMode mode) const { + ApkAssetsCookie cookie, Asset::AccessMode mode) { ATRACE_CALL(); if (cookie < 0 || static_cast(cookie) >= apk_assets_.size()) { return {}; @@ -296,13 +264,12 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, - bool /*stop_at_first_match*/, - FindEntryResult* out_entry) const { + bool stop_at_first_match, FindEntryResult* out_entry) { // Might use this if density_override != 0. ResTable_config density_override_config; // Select our configuration or generate a density override configuration. - const ResTable_config* desired_config = &configuration_; + ResTable_config* desired_config = &configuration_; if (density_override != 0 && density_override != configuration_.density) { density_override_config = configuration_; density_override_config.density = density_override; @@ -316,135 +283,53 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const uint32_t package_id = get_package_id(resid); const uint8_t type_idx = get_type_id(resid) - 1; - const uint16_t entry_idx = get_entry_id(resid); + const uint16_t entry_id = get_entry_id(resid); - const uint8_t package_idx = package_ids_[package_id]; - if (package_idx == 0xff) { + const uint8_t idx = package_ids_[package_id]; + if (idx == 0xff) { LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); return kInvalidCookie; } - const PackageGroup& package_group = package_groups_[package_idx]; - const size_t package_count = package_group.packages_.size(); - + FindEntryResult best_entry; ApkAssetsCookie best_cookie = kInvalidCookie; - const LoadedPackage* best_package = nullptr; - const ResTable_type* best_type = nullptr; - const ResTable_config* best_config = nullptr; - ResTable_config best_config_copy; - uint32_t best_offset = 0u; - uint32_t type_flags = 0u; + uint32_t cumulated_flags = 0u; - // If desired_config is the same as the set configuration, then we can use our filtered list - // and we don't need to match the configurations, since they already matched. - const bool use_fast_path = desired_config == &configuration_; - - for (size_t pi = 0; pi < package_count; pi++) { - const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; - const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_; - ApkAssetsCookie cookie = package_group.cookies_[pi]; - - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx); - if (UNLIKELY(type_spec == nullptr)) { + const PackageGroup& package_group = package_groups_[idx]; + const size_t package_count = package_group.packages_.size(); + FindEntryResult current_entry; + for (size_t i = 0; i < package_count; i++) { + const LoadedPackage* loaded_package = package_group.packages_[i]; + if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) { continue; } - uint16_t local_entry_idx = entry_idx; + cumulated_flags |= current_entry.type_flags; - // If there is an IDMAP supplied with this package, translate the entry ID. - if (type_spec->idmap_entries != nullptr) { - if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) { - // There is no mapping, so the resource is not meant to be in this overlay package. - continue; - } - } - - type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx); - - // If the package is an overlay, then even configurations that are the same MUST be chosen. - const bool package_is_overlay = loaded_package->IsOverlay(); - - const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; - if (use_fast_path) { - const std::vector& candidate_configs = filtered_group.configurations; - const size_t type_count = candidate_configs.size(); - for (uint32_t i = 0; i < type_count; i++) { - const ResTable_config& this_config = candidate_configs[i]; - - // We can skip calling ResTable_config::match() because we know that all candidate - // configurations that do NOT match have been filtered-out. - if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || - (package_is_overlay && this_config.compare(*best_config) == 0)) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const ResTable_type* type_chunk = filtered_group.types[i]; - const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } - - best_cookie = cookie; - best_package = loaded_package; - best_type = type_chunk; - best_config = &this_config; - best_offset = offset; - } - } - } else { - // This is the slower path, which doesn't use the filtered list of configurations. - // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness - // and fill in any new fields that did not exist when the APK was compiled. - // Furthermore when selecting configurations we can't just record the pointer to the - // ResTable_config, we must copy it. - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config this_config; - this_config.copyFromDtoH((*iter)->config); - - if (this_config.match(*desired_config)) { - if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || - (package_is_overlay && this_config.compare(*best_config) == 0)) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } - - best_cookie = cookie; - best_package = loaded_package; - best_type = *iter; - best_config_copy = this_config; - best_config = &best_config_copy; - best_offset = offset; - } - } + const ResTable_config* current_config = current_entry.config; + const ResTable_config* best_config = best_entry.config; + if (best_cookie == kInvalidCookie || + current_config->isBetterThan(*best_config, desired_config) || + (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) { + best_entry = current_entry; + best_cookie = package_group.cookies_[i]; + if (stop_at_first_match) { + break; } } } - if (UNLIKELY(best_cookie == kInvalidCookie)) { + if (best_cookie == kInvalidCookie) { return kInvalidCookie; } - const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset); - if (UNLIKELY(best_entry == nullptr)) { - return kInvalidCookie; - } - - out_entry->entry = best_entry; - out_entry->config = *best_config; - out_entry->type_flags = type_flags; - out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); - out_entry->entry_string_ref = - StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); + *out_entry = best_entry; out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; + out_entry->type_flags = cumulated_flags; return best_cookie; } -bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { +bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { ATRACE_CALL(); FindEntryResult entry; @@ -454,8 +339,7 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons return false; } - const LoadedPackage* package = - apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); + const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid); if (package == nullptr) { return false; } @@ -483,7 +367,7 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons return true; } -bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { +bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); @@ -497,7 +381,7 @@ bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) const { + uint32_t* out_flags) { ATRACE_CALL(); FindEntryResult entry; @@ -516,7 +400,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Create a reference since we can't represent this complex type as a Res_value. out_value->dataType = Res_value::TYPE_REFERENCE; out_value->data = resid; - *out_selected_config = entry.config; + *out_selected_config = *entry.config; *out_flags = entry.type_flags; return cookie; } @@ -528,7 +412,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Convert the package ID to the runtime assigned package ID. entry.dynamic_ref_table->lookupResourceValue(out_value); - *out_selected_config = entry.config; + *out_selected_config = *entry.config; *out_flags = entry.type_flags; return cookie; } @@ -536,7 +420,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) const { + uint32_t* out_last_reference) { ATRACE_CALL(); constexpr const int kMaxIterations = 20; @@ -604,8 +488,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Attributes, arrays, etc don't have a resource id as the name. They specify // other data, which would be wrong to change via a lookup. if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, - resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); return nullptr; } } @@ -637,8 +520,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { const ResolvedBag* parent_bag = GetBag(parent_resid); if (parent_bag == nullptr) { // Failed to get the parent that should exist. - LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, - resid); + LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid); return nullptr; } @@ -657,8 +539,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, - resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid); return nullptr; } } @@ -697,8 +578,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, - resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); return nullptr; } } @@ -754,7 +634,7 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const std::string& fallback_type, - const std::string& fallback_package) const { + const std::string& fallback_package) { StringPiece package_name, type, entry; if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { return 0u; @@ -786,8 +666,7 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const static std::u16string kAttrPrivate16 = u"^attr-private"; for (const PackageGroup& package_group : package_groups_) { - for (const ConfiguredPackage& package_impl : package_group.packages_) { - const LoadedPackage* package = package_impl.loaded_package_; + for (const LoadedPackage* package : package_group.packages_) { if (package_name != package->GetPackageName()) { // All packages in the same group are expected to have the same package name. break; @@ -809,32 +688,6 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, return 0u; } -void AssetManager2::RebuildFilterList() { - for (PackageGroup& group : package_groups_) { - for (ConfiguredPackage& impl : group.packages_) { - // Destroy it. - impl.filtered_configs_.~ByteBucketArray(); - - // Re-create it. - new (&impl.filtered_configs_) ByteBucketArray(); - - // Create the filters here. - impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) { - FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index); - const auto iter_end = spec->types + spec->type_count; - for (auto iter = spec->types; iter != iter_end; ++iter) { - ResTable_config this_config; - this_config.copyFromDtoH((*iter)->config); - if (this_config.match(configuration_)) { - group.configurations.push_back(this_config); - group.types.push_back(*iter); - } - } - }); - } - } -} - void AssetManager2::InvalidateCaches(uint32_t diff) { if (diff == 0xffffffffu) { // Everything must go. @@ -1015,7 +868,7 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_type_spec_flags, - uint32_t* out_last_ref) const { + uint32_t* out_last_ref) { if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) { uint32_t new_flags; cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags); diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 1d2c597c4c8c7..e08848f891f6b 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -44,6 +44,44 @@ namespace android { constexpr const static int kAppPackageId = 0x7f; +// Element of a TypeSpec array. See TypeSpec. +struct Type { + // The configuration for which this type defines entries. + // This is already converted to host endianness. + ResTable_config configuration; + + // Pointer to the mmapped data where entry definitions are kept. + const ResTable_type* type; +}; + +// TypeSpec is going to be immediately proceeded by +// an array of Type structs, all in the same block of memory. +struct TypeSpec { + // Pointer to the mmapped data where flags are kept. + // Flags denote whether the resource entry is public + // and under which configurations it varies. + const ResTable_typeSpec* type_spec; + + // Pointer to the mmapped data where the IDMAP mappings for this type + // exist. May be nullptr if no IDMAP exists. + const IdmapEntry_header* idmap_entries; + + // The number of types that follow this struct. + // There is a type for each configuration + // that entries are defined for. + size_t type_count; + + // Trick to easily access a variable number of Type structs + // proceeding this struct, and to ensure their alignment. + const Type types[0]; +}; + +// TypeSpecPtr points to the block of memory that holds +// a TypeSpec struct, followed by an array of Type structs. +// TypeSpecPtr is a managed pointer that knows how to delete +// itself. +using TypeSpecPtr = util::unique_cptr; + namespace { // Builder that helps accumulate Type structs and then create a single @@ -57,22 +95,21 @@ class TypeSpecPtrBuilder { } void AddType(const ResTable_type* type) { - types_.push_back(type); + ResTable_config config; + config.copyFromDtoH(type->config); + types_.push_back(Type{config, type}); } TypeSpecPtr Build() { // Check for overflow. - using ElementType = const ResTable_type*; - if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(ElementType) < - types_.size()) { + if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) { return {}; } - TypeSpec* type_spec = - (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType))); + TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type))); type_spec->type_spec = header_; type_spec->idmap_entries = idmap_header_; type_spec->type_count = types_.size(); - memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType)); + memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type)); return TypeSpecPtr(type_spec); } @@ -81,7 +118,7 @@ class TypeSpecPtrBuilder { const ResTable_typeSpec* header_; const IdmapEntry_header* idmap_header_; - std::vector types_; + std::vector types_; }; } // namespace @@ -125,17 +162,18 @@ static bool VerifyResTableType(const ResTable_type* header) { return true; } -static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) { +static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset, + size_t entry_idx) { // Check that the offset is aligned. if (entry_offset & 0x03) { - LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned."; + LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned."; return false; } // Check that the offset doesn't overflow. if (entry_offset > std::numeric_limits::max() - dtohl(type->entriesStart)) { // Overflow in offset. - LOG(ERROR) << "Entry at offset " << entry_offset << " is too large."; + LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large."; return false; } @@ -143,7 +181,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset entry_offset += dtohl(type->entriesStart); if (entry_offset > chunk_size - sizeof(ResTable_entry)) { - LOG(ERROR) << "Entry at offset " << entry_offset + LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large. No room for ResTable_entry."; return false; } @@ -153,13 +191,13 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t entry_size = dtohs(entry->size); if (entry_size < sizeof(*entry)) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset + LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx << " is too small."; return false; } if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset + LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx << " is too large."; return false; } @@ -167,7 +205,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset if (entry_size < sizeof(ResTable_map_entry)) { // There needs to be room for one Res_value struct. if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) { - LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset + LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << entry_idx << " for type " << (int)type->id << "."; return false; } @@ -176,12 +214,12 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset reinterpret_cast(reinterpret_cast(entry) + entry_size); const size_t value_size = dtohs(value->size); if (value_size < sizeof(Res_value)) { - LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small."; + LOG(ERROR) << "Res_value at index " << entry_idx << " is too small."; return false; } if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { - LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset + LOG(ERROR) << "Res_value size " << value_size << " at index " << entry_idx << " is too large."; return false; } @@ -190,76 +228,117 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t map_entry_count = dtohl(map->count); size_t map_entries_start = entry_offset + entry_size; if (map_entries_start & 0x03) { - LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset."; + LOG(ERROR) << "Map entries at index " << entry_idx << " start at unaligned offset."; return false; } // Each entry is sizeof(ResTable_map) big. if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) { - LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << "."; + LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << entry_idx << "."; return false; } } return true; } -const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk, - uint16_t entry_index) { - uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index); - if (entry_offset == ResTable_type::NO_ENTRY) { - return nullptr; - } - return GetEntryFromOffset(type_chunk, entry_offset); -} +bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx, + const ResTable_config& config, FindEntryResult* out_entry) const { + const ResTable_config* best_config = nullptr; + const ResTable_type* best_type = nullptr; + uint32_t best_offset = 0; -uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const size_t entry_count = dtohl(type_chunk->entryCount); - const size_t offsets_offset = dtohs(type_chunk->header.headerSize); + for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) { + const Type* type = &type_spec_ptr->types[i]; + const ResTable_type* type_chunk = type->type; - // Check if there is the desired entry in this type. + if (type->configuration.match(config) && + (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const size_t entry_count = dtohl(type_chunk->entryCount); + const size_t offsets_offset = dtohs(type_chunk->header.headerSize); - if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { - // This is encoded as a sparse map, so perform a binary search. - const ResTable_sparseTypeEntry* sparse_indices = - reinterpret_cast( + // Check if there is the desired entry in this type. + + if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { + // This is encoded as a sparse map, so perform a binary search. + const ResTable_sparseTypeEntry* sparse_indices = + reinterpret_cast( + reinterpret_cast(type_chunk) + offsets_offset); + const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; + const ResTable_sparseTypeEntry* result = + std::lower_bound(sparse_indices, sparse_indices_end, entry_idx, + [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { + return dtohs(entry.idx) < entry_idx; + }); + + if (result == sparse_indices_end || dtohs(result->idx) != entry_idx) { + // No entry found. + continue; + } + + // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as + // the real offset divided by 4. + best_offset = uint32_t{dtohs(result->offset)} * 4u; + } else { + if (entry_idx >= entry_count) { + // This entry cannot be here. + continue; + } + + const uint32_t* entry_offsets = reinterpret_cast( reinterpret_cast(type_chunk) + offsets_offset); - const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; - const ResTable_sparseTypeEntry* result = - std::lower_bound(sparse_indices, sparse_indices_end, entry_index, - [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { - return dtohs(entry.idx) < entry_idx; - }); + const uint32_t offset = dtohl(entry_offsets[entry_idx]); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } - if (result == sparse_indices_end || dtohs(result->idx) != entry_index) { - // No entry found. - return ResTable_type::NO_ENTRY; + // There is an entry for this resource, record it. + best_offset = offset; + } + + best_config = &type->configuration; + best_type = type_chunk; } - - // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as - // the real offset divided by 4. - return uint32_t{dtohs(result->offset)} * 4u; } - // This type is encoded as a dense array. - if (entry_index >= entry_count) { - // This entry cannot be here. - return ResTable_type::NO_ENTRY; + if (best_type == nullptr) { + return false; } - const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - return dtohl(entry_offsets[entry_index]); + if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) { + return false; + } + + const ResTable_entry* best_entry = reinterpret_cast( + reinterpret_cast(best_type) + best_offset + dtohl(best_type->entriesStart)); + + const uint32_t* flags = reinterpret_cast(type_spec_ptr->type_spec + 1); + out_entry->type_flags = dtohl(flags[entry_idx]); + out_entry->entry = best_entry; + out_entry->config = best_config; + out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1); + out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index)); + return true; } -const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk, - uint32_t offset) { - if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) { - return nullptr; +bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, + FindEntryResult* out_entry) const { + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; + if (UNLIKELY(ptr == nullptr)) { + return false; } - return reinterpret_cast(reinterpret_cast(type_chunk) + - offset + dtohl(type_chunk->entriesStart)); + + // If there is an IDMAP supplied with this package, translate the entry ID. + if (ptr->idmap_entries != nullptr) { + if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) { + // There is no mapping, so the resource is not meant to be in this overlay package. + return false; + } + } + return FindEntry(ptr, entry_idx, config, out_entry); } void LoadedPackage::CollectConfigurations(bool exclude_mipmap, @@ -267,7 +346,7 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, const static std::u16string kMipMap = u"mipmap"; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const TypeSpecPtr& type_spec = type_specs_[i]; + const util::unique_cptr& type_spec = type_specs_[i]; if (type_spec != nullptr) { if (exclude_mipmap) { const int type_idx = type_spec->type_spec->id - 1; @@ -288,11 +367,8 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, } } - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config config; - config.copyFromDtoH((*iter)->config); - out_configs->insert(config); + for (size_t j = 0; j < type_spec->type_count; j++) { + out_configs->insert(type_spec->types[j].configuration); } } } @@ -302,12 +378,10 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set* out char temp_locale[RESTABLE_MAX_LOCALE_LEN]; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const TypeSpecPtr& type_spec = type_specs_[i]; + const util::unique_cptr& type_spec = type_specs_[i]; if (type_spec != nullptr) { - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config configuration; - configuration.copyFromDtoH((*iter)->config); + for (size_t j = 0; j < type_spec->type_count; j++) { + const ResTable_config& configuration = type_spec->types[j].configuration; if (configuration.locale != 0) { configuration.getBcp47Locale(temp_locale, canonicalize); std::string locale(temp_locale); @@ -335,17 +409,17 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - const ResTable_type* type = *iter; - size_t entry_count = dtohl(type->entryCount); + for (size_t ti = 0; ti < type_spec->type_count; ti++) { + const Type* type = &type_spec->types[ti]; + size_t entry_count = dtohl(type->type->entryCount); for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type) + dtohs(type->header.headerSize)); + reinterpret_cast(type->type) + dtohs(type->type->header.headerSize)); const uint32_t offset = dtohl(entry_offsets[entry_idx]); if (offset != ResTable_type::NO_ENTRY) { - const ResTable_entry* entry = reinterpret_cast( - reinterpret_cast(type) + dtohl(type->entriesStart) + offset); + const ResTable_entry* entry = + reinterpret_cast(reinterpret_cast(type->type) + + dtohl(type->type->entriesStart) + offset); if (dtohl(entry->key.index) == static_cast(key_idx)) { // The package ID will be overridden by the caller (due to runtime assignment of package // IDs for shared libraries). @@ -357,7 +431,8 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } -const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { +const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { + const uint8_t package_id = get_package_id(resid); for (const auto& loaded_package : packages_) { if (loaded_package->GetPackageId() == package_id) { return loaded_package.get(); @@ -605,6 +680,26 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, return std::move(loaded_package); } +bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, + FindEntryResult* out_entry) const { + ATRACE_CALL(); + + const uint8_t package_id = get_package_id(resid); + const uint8_t type_id = get_type_id(resid); + const uint16_t entry_id = get_entry_id(resid); + + if (UNLIKELY(type_id == 0)) { + LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); + return false; + } + + for (const auto& loaded_package : packages_) { + if (loaded_package->GetPackageId() == package_id) { + return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry); + } + } + return false; +} bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library) { diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index ef08897d997a6..b033137b47642 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -69,8 +69,6 @@ struct ResolvedBag { Entry entries[0]; }; -struct FindEntryResult; - // AssetManager2 is the main entry point for accessing assets and resources. // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets. class AssetManager2 { @@ -129,7 +127,7 @@ class AssetManager2 { // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap' // will be excluded from the list. std::set GetResourceConfigurations(bool exclude_system = false, - bool exclude_mipmap = false) const; + bool exclude_mipmap = false); // Returns all the locales for which there are resources defined. This includes resource // locales in all the ApkAssets set for this AssetManager. @@ -138,24 +136,24 @@ class AssetManager2 { // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized // and de-duped in the resulting list. std::set GetResourceLocales(bool exclude_system = false, - bool merge_equivalent_languages = false) const; + bool merge_equivalent_languages = false); // Searches the set of APKs loaded by this AssetManager and opens the first one found located // in the assets/ directory. // `mode` controls how the file is opened. // // NOTE: The loaded APKs are searched in reverse order. - std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode) const; + std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode); // Opens a file within the assets/ directory of the APK specified by `cookie`. // `mode` controls how the file is opened. std::unique_ptr Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) const; + Asset::AccessMode mode); // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded. // The entries are sorted by their ASCII name. - std::unique_ptr OpenDir(const std::string& dirname) const; + std::unique_ptr OpenDir(const std::string& dirname); // Searches the set of APKs loaded by this AssetManager and opens the first one found. // `mode` controls how the file is opened. @@ -163,24 +161,24 @@ class AssetManager2 { // // NOTE: The loaded APKs are searched in reverse order. std::unique_ptr OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie = nullptr) const; + ApkAssetsCookie* out_cookie = nullptr); // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened. // This is typically used to open a specific AndroidManifest.xml, or a binary XML file // referenced by a resource lookup with GetResource(). std::unique_ptr OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) const; + Asset::AccessMode mode); // Populates the `out_name` parameter with resource name information. // Utf8 strings are preferred, and only if they are unavailable are // the Utf16 variants populated. // Returns false if the resource was not found or the name was missing/corrupt. - bool GetResourceName(uint32_t resid, ResourceName* out_name) const; + bool GetResourceName(uint32_t resid, ResourceName* out_name); // Populates `out_flags` with the bitmask of configuration axis that this resource varies with. // See ResTable_config for the list of configuration axis. // Returns false if the resource was not found. - bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const; + bool GetResourceFlags(uint32_t resid, uint32_t* out_flags); // Finds the resource ID assigned to `resource_name`. // `resource_name` must be of the form '[package:][type/]entry'. @@ -188,7 +186,7 @@ class AssetManager2 { // If no type is specified in `resource_name`, then `fallback_type` is used as the type. // Returns 0x0 if no resource by that name was found. uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {}, - const std::string& fallback_package = {}) const; + const std::string& fallback_package = {}); // Retrieves the best matching resource with ID `resid`. The resource value is filled into // `out_value` and the configuration for the selected value is populated in `out_selected_config`. @@ -201,7 +199,7 @@ class AssetManager2 { // this function logs if the resource was a map/bag type before returning kInvalidCookie. ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) const; + uint32_t* out_flags); // Resolves the resource reference in `in_out_value` if the data type is // Res_value::TYPE_REFERENCE. @@ -217,7 +215,7 @@ class AssetManager2 { // it was not found. ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) const; + uint32_t* out_last_reference); // Retrieves the best matching bag/map resource with ID `resid`. // This method will resolve all parent references for this bag and merge keys with the child. @@ -235,9 +233,9 @@ class AssetManager2 { std::unique_ptr NewTheme(); template - void ForEachPackage(Func func) const { + void ForEachPackage(Func func) { for (const PackageGroup& package_group : package_groups_) { - func(package_group.packages_.front().loaded_package_->GetPackageName(), + func(package_group.packages_.front()->GetPackageName(), package_group.dynamic_ref_table.mAssignedPackageId); } } @@ -262,7 +260,7 @@ class AssetManager2 { // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, - FindEntryResult* out_entry) const; + FindEntryResult* out_entry); // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. @@ -272,43 +270,13 @@ class AssetManager2 { // bitmask `diff`. void InvalidateCaches(uint32_t diff); - // Triggers the re-construction of lists of types that match the set configuration. - // This should always be called when mutating the AssetManager's configuration or ApkAssets set. - void RebuildFilterList(); - // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector apk_assets_; - // A collection of configurations and their associated ResTable_type that match the current - // AssetManager configuration. - struct FilteredConfigGroup { - std::vector configurations; - std::vector types; - }; - - // Represents an single package. - struct ConfiguredPackage { - // A pointer to the immutable, loaded package info. - const LoadedPackage* loaded_package_; - - // A mutable AssetManager-specific list of configurations that match the AssetManager's - // current configuration. This is used as an optimization to avoid checking every single - // candidate configuration when looking up resources. - ByteBucketArray filtered_configs_; - }; - - // Represents a logical package, which can be made up of many individual packages. Each package - // in a PackageGroup shares the same package name and package ID. struct PackageGroup { - // The set of packages that make-up this group. - std::vector packages_; - - // The cookies associated with each package in the group. They share the same order as - // packages_. + std::vector packages_; std::vector cookies_; - - // A library reference table that contains build-package ID to runtime-package ID mappings. DynamicRefTable dynamic_ref_table; }; @@ -382,7 +350,7 @@ class Theme { ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config = nullptr, uint32_t* in_out_type_spec_flags = nullptr, - uint32_t* out_last_ref = nullptr) const; + uint32_t* out_last_ref = nullptr); private: DISALLOW_COPY_AND_ASSIGN(Theme); diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 35ae5fcd9e7bb..1775f5070f4ea 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -41,40 +41,33 @@ class DynamicPackageEntry { int package_id = 0; }; -// TypeSpec is going to be immediately proceeded by -// an array of Type structs, all in the same block of memory. -struct TypeSpec { - // Pointer to the mmapped data where flags are kept. - // Flags denote whether the resource entry is public - // and under which configurations it varies. - const ResTable_typeSpec* type_spec; +struct FindEntryResult { + // A pointer to the resource table entry for this resource. + // If the size of the entry is > sizeof(ResTable_entry), it can be cast to + // a ResTable_map_entry and processed as a bag/map. + const ResTable_entry* entry; - // Pointer to the mmapped data where the IDMAP mappings for this type - // exist. May be nullptr if no IDMAP exists. - const IdmapEntry_header* idmap_entries; + // The configuration for which the resulting entry was defined. This points to a structure that + // is already swapped to host endianness. + const ResTable_config* config; - // The number of types that follow this struct. - // There is a type for each configuration that entries are defined for. - size_t type_count; + // The bitmask of configuration axis with which the resource value varies. + uint32_t type_flags; - // Trick to easily access a variable number of Type structs - // proceeding this struct, and to ensure their alignment. - const ResTable_type* types[0]; + // The dynamic package ID map for the package from which this resource came from. + const DynamicRefTable* dynamic_ref_table; - inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const { - if (entry_index >= dtohl(type_spec->entryCount)) { - return 0u; - } + // The string pool reference to the type's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef type_string_ref; - const uint32_t* flags = reinterpret_cast(type_spec + 1); - return flags[entry_index]; - } + // The string pool reference to the entry's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef entry_string_ref; }; -// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of -// ResTable_type pointers. -// TypeSpecPtr is a managed pointer that knows how to delete itself. -using TypeSpecPtr = util::unique_cptr; +struct TypeSpec; +class LoadedArsc; class LoadedPackage { public: @@ -84,6 +77,9 @@ class LoadedPackage { ~LoadedPackage(); + bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, + FindEntryResult* out_entry) const; + // Finds the entry with the specified type name and entry name. The names are in UTF-16 because // the underlying ResStringPool API expects this. For now this is acceptable, but since // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. @@ -91,12 +87,6 @@ class LoadedPackage { // for patching the correct package ID to the resource ID. uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; - static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index); - - static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index); - - static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset); - // Returns the string pool where type names are stored. inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; @@ -146,32 +136,14 @@ class LoadedPackage { // before being inserted into the set. This may cause some equivalent locales to de-dupe. void CollectLocales(bool canonicalize, std::set* out_locales) const; - // type_idx is TT - 1 from 0xPPTTEEEE. - inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const { - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - return type_specs_[type_index - type_id_offset_].get(); - } - - template - void ForEachTypeSpec(Func f) const { - for (size_t i = 0; i < type_specs_.size(); i++) { - const TypeSpecPtr& ptr = type_specs_[i]; - if (ptr != nullptr) { - uint8_t type_id = ptr->type_spec->id; - if (ptr->idmap_entries != nullptr) { - type_id = ptr->idmap_entries->target_type_id; - } - f(ptr.get(), type_id - 1); - } - } - } - private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); LoadedPackage(); + bool FindEntry(const util::unique_cptr& type_spec_ptr, uint16_t entry_idx, + const ResTable_config& config, FindEntryResult* out_entry) const; + ResStringPool type_string_pool_; ResStringPool key_string_pool_; std::string package_name_; @@ -181,7 +153,7 @@ class LoadedPackage { bool system_ = false; bool overlay_ = false; - ByteBucketArray type_specs_; + ByteBucketArray> type_specs_; std::vector dynamic_package_map_; }; @@ -209,20 +181,25 @@ class LoadedArsc { return &global_string_pool_; } - // Gets a pointer to the package with the specified package ID, or nullptr if no such package - // exists. - const LoadedPackage* GetPackageById(uint8_t package_id) const; + // Finds the resource with ID `resid` with the best value for configuration `config`. + // The parameter `out_entry` will be filled with the resulting resource entry. + // The resource entry can be a simple entry (ResTable_entry) or a complex bag + // (ResTable_entry_map). + bool FindEntry(uint32_t resid, const ResTable_config& config, FindEntryResult* out_entry) const; - // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. - inline const std::vector>& GetPackages() const { - return packages_; - } + // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist. + const LoadedPackage* GetPackageForId(uint32_t resid) const; // Returns true if this is a system provided resource. inline bool IsSystem() const { return system_; } + // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. + inline const std::vector>& GetPackages() const { + return packages_; + } + private: DISALLOW_COPY_AND_ASSIGN(LoadedArsc); diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index e2b9f0040989a..6c43a67e602f2 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -26,56 +26,58 @@ using ::android::base::unique_fd; using ::com::android::basic::R; -using ::testing::Eq; -using ::testing::Ge; -using ::testing::NotNull; -using ::testing::SizeIs; -using ::testing::StrEq; namespace android { TEST(ApkAssetsTest, LoadApk) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); - ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); + ASSERT_NE(nullptr, loaded_arsc); + + const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); + ASSERT_NE(nullptr, loaded_package); + + std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); + ASSERT_NE(nullptr, asset); } TEST(ApkAssetsTest, LoadApkFromFd) { const std::string path = GetTestDataPath() + "/basic/basic.apk"; unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY)); - ASSERT_THAT(fd.get(), Ge(0)); + ASSERT_GE(fd.get(), 0); std::unique_ptr loaded_apk = ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); - ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); + ASSERT_NE(nullptr, loaded_arsc); + + const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); + ASSERT_NE(nullptr, loaded_package); + + std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); + ASSERT_NE(nullptr, asset); } TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_THAT(loaded_apk, NotNull()); - + ASSERT_NE(nullptr, loaded_apk); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); + ASSERT_NE(nullptr, loaded_arsc); + ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic()); loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); + ASSERT_NE(nullptr, loaded_arsc); + ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); } @@ -84,22 +86,19 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { ResTable target_table; const std::string target_path = GetTestDataPath() + "/basic/basic.apk"; ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents)); - ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/), - Eq(NO_ERROR)); + ASSERT_EQ(NO_ERROR, target_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); ResTable overlay_table; const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk"; ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents)); - ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/), - Eq(NO_ERROR)); + ASSERT_EQ(NO_ERROR, overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); util::unique_cptr idmap_data; void* temp_data; size_t idmap_len; - ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), - overlay_path.c_str(), &temp_data, &idmap_len), - Eq(NO_ERROR)); + ASSERT_EQ(NO_ERROR, target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), + overlay_path.c_str(), &temp_data, &idmap_len)); idmap_data.reset(temp_data); TemporaryFile tf; @@ -109,30 +108,37 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { // Open something so that the destructor of TemporaryFile closes a valid fd. tf.fd = open("/dev/null", O_WRONLY); - ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull()); + std::unique_ptr loaded_overlay_apk = ApkAssets::LoadOverlay(tf.path); + ASSERT_NE(nullptr, loaded_overlay_apk); } TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); - { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } + { + std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, assets); + } - { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } + { + std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, assets); + } } TEST(ApkAssetsTest, OpenUncompressedAssetFd) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN); - ASSERT_THAT(asset, NotNull()); + ASSERT_NE(nullptr, asset); off64_t start, length; unique_fd fd(asset->openFileDescriptor(&start, &length)); - ASSERT_THAT(fd.get(), Ge(0)); + EXPECT_GE(fd.get(), 0); lseek64(fd.get(), start, SEEK_SET); @@ -140,7 +146,7 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) { buffer.resize(length); ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length)); - EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n")); + EXPECT_EQ("This should be uncompressed.\n\n", buffer); } } // namespace android diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index bedebd66cb2f0..37ddafb14fd35 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -16,8 +16,6 @@ #include "androidfw/LoadedArsc.h" -#include "androidfw/ResourceUtils.h" - #include "TestHelpers.h" #include "data/basic/R.h" #include "data/libclient/R.h" @@ -29,13 +27,6 @@ namespace basic = com::android::basic; namespace libclient = com::android::libclient; namespace sparse = com::android::sparse; -using ::testing::Eq; -using ::testing::Ge; -using ::testing::IsNull; -using ::testing::NotNull; -using ::testing::SizeIs; -using ::testing::StrEq; - namespace android { TEST(LoadedArscTest, LoadSinglePackageArsc) { @@ -44,24 +35,39 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(app::R::string::string_one)); - ASSERT_THAT(package, NotNull()); - EXPECT_THAT(package->GetPackageName(), StrEq("com.android.app")); - EXPECT_THAT(package->GetPackageId(), Eq(0x7f)); + const std::vector>& packages = loaded_arsc->GetPackages(); + ASSERT_EQ(1u, packages.size()); + EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName()); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); - const uint8_t type_index = get_type_id(app::R::string::string_one) - 1; - const uint16_t entry_index = get_entry_id(app::R::string::string_one); + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 24; - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); + FindEntryResult entry; - const ResTable_type* type = type_spec->types[0]; - ASSERT_THAT(type, NotNull()); - ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); + ASSERT_TRUE(loaded_arsc->FindEntry(app::R::string::string_one, config, &entry)); + ASSERT_NE(nullptr, entry.entry); +} + +TEST(LoadedArscTest, FindDefaultEntry) { + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); + + std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + ASSERT_NE(nullptr, loaded_arsc); + + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + desired_config.language[0] = 'd'; + desired_config.language[1] = 'e'; + + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry)); + ASSERT_NE(nullptr, entry.entry); } TEST(LoadedArscTest, LoadSparseEntryApp) { @@ -70,22 +76,15 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9)); - ASSERT_THAT(package, NotNull()); + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 26; - const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1; - const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9); - - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - - const ResTable_type* type = type_spec->types[0]; - ASSERT_THAT(type, NotNull()); - ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry)); + ASSERT_NE(nullptr, entry.entry); } TEST(LoadedArscTest, LoadSharedLibrary) { @@ -94,13 +93,14 @@ TEST(LoadedArscTest, LoadSharedLibrary) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_THAT(packages, SizeIs(1u)); + ASSERT_EQ(1u, packages.size()); + EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.lib_one")); - EXPECT_THAT(packages[0]->GetPackageId(), Eq(0)); + EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName()); + EXPECT_EQ(0, packages[0]->GetPackageId()); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); @@ -114,23 +114,25 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_THAT(packages, SizeIs(1u)); + ASSERT_EQ(1u, packages.size()); + EXPECT_FALSE(packages[0]->IsDynamic()); - EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.libclient")); - EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); + EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName()); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); // The library has two dependencies. - ASSERT_THAT(dynamic_pkg_map, SizeIs(2u)); - EXPECT_THAT(dynamic_pkg_map[0].package_name, StrEq("com.android.lib_one")); - EXPECT_THAT(dynamic_pkg_map[0].package_id, Eq(0x02)); + ASSERT_EQ(2u, dynamic_pkg_map.size()); - EXPECT_THAT(dynamic_pkg_map[1].package_name, StrEq("com.android.lib_two")); - EXPECT_THAT(dynamic_pkg_map[1].package_id, Eq(0x03)); + EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name); + EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id); + + EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name); + EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id); } TEST(LoadedArscTest, LoadAppAsSharedLibrary) { @@ -141,12 +143,13 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, true /*load_as_shared_library*/); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_THAT(packages, SizeIs(1u)); + ASSERT_EQ(1u, packages.size()); + EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); } TEST(LoadedArscTest, LoadFeatureSplit) { @@ -154,27 +157,21 @@ TEST(LoadedArscTest, LoadFeatureSplit) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(basic::R::string::test3)); - ASSERT_THAT(package, NotNull()); + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); - uint8_t type_index = get_type_id(basic::R::string::test3) - 1; - uint8_t entry_index = get_entry_id(basic::R::string::test3); - - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry)); size_t len; - const char16_t* type_name16 = - package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len); - ASSERT_THAT(type_name16, NotNull()); - EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string")); + const char16_t* type_name16 = entry.type_string_ref.string16(&len); + ASSERT_NE(nullptr, type_name16); + ASSERT_NE(0u, len); - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); + std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len)); + EXPECT_EQ(std::string("string"), type_name); } class MockLoadedIdmap : public LoadedIdmap { @@ -202,33 +199,23 @@ class MockLoadedIdmap : public LoadedIdmap { }; TEST(LoadedArscTest, LoadOverlay) { - std::string contents; + std::string contents, overlay_contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", - &contents)); + &overlay_contents)); MockLoadedIdmap loaded_idmap; std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(contents), &loaded_idmap); - ASSERT_THAT(loaded_arsc, NotNull()); + LoadedArsc::Load(StringPiece(overlay_contents), &loaded_idmap); + ASSERT_NE(nullptr, loaded_arsc); - const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u); - ASSERT_THAT(package, NotNull()); + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); - - // The entry being overlaid doesn't exist at the original entry index. - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull()); - - // Since this is an overlay, the actual entry ID must be mapped. - ASSERT_THAT(type_spec->idmap_entries, NotNull()); - uint16_t target_entry_id = 0u; - ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id)); - ASSERT_THAT(target_entry_id, Eq(0x0u)); - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull()); + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry)); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h index df0c642f45653..43a995536d89e 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -20,7 +20,6 @@ #include #include "androidfw/ResourceTypes.h" -#include "gmock/gmock.h" #include "gtest/gtest.h" #include "CommonHelpers.h" From 6012809abaccd73087c84afa0e9958d6ade6222c Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Tue, 23 Jan 2018 03:16:33 -0800 Subject: [PATCH 068/226] Revert "libandroidfw: Add ApplyStyle and SetConfiguration benchmark" This reverts commit 62f17129a0c519fe3e591be102b1863024cea3a4. (cherry picked from commit fae57eb4b8b86271c79facc3e884e02f5156396b) Change-Id: I39c5fe4a3ad7b2bd6503385f92eb2e1b9979a091 --- libs/androidfw/Android.bp | 1 - libs/androidfw/tests/AssetManager2_bench.cpp | 59 +----- .../tests/AttributeResolution_bench.cpp | 175 ------------------ libs/androidfw/tests/BenchmarkHelpers.cpp | 12 +- libs/androidfw/tests/data/basic/R.h | 2 - libs/androidfw/tests/data/basic/basic.apk | Bin 5260 -> 3124 bytes .../tests/data/basic/res/layout/layout.xml | 25 --- .../tests/data/basic/res/values/values.xml | 13 -- 8 files changed, 13 insertions(+), 274 deletions(-) delete mode 100644 libs/androidfw/tests/AttributeResolution_bench.cpp delete mode 100644 libs/androidfw/tests/data/basic/res/layout/layout.xml diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 7c9078b164a2f..251b2e773cfb3 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -171,7 +171,6 @@ cc_benchmark { // Actual benchmarks. "tests/AssetManager2_bench.cpp", - "tests/AttributeResolution_bench.cpp", "tests/SparseEntry_bench.cpp", "tests/Theme_bench.cpp", ], diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index 437e147729648..85e8f25394e9e 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -81,18 +81,17 @@ static void BM_AssetManagerLoadFrameworkAssetsOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld); -static void BM_AssetManagerGetResource(benchmark::State& state, uint32_t resid) { - GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, state); +static void BM_AssetManagerGetResource(benchmark::State& state) { + GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, + basic::R::integer::number1, state); } -BENCHMARK_CAPTURE(BM_AssetManagerGetResource, number1, basic::R::integer::number1); -BENCHMARK_CAPTURE(BM_AssetManagerGetResource, deep_ref, basic::R::integer::deep_ref); +BENCHMARK(BM_AssetManagerGetResource); -static void BM_AssetManagerGetResourceOld(benchmark::State& state, uint32_t resid) { - GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, - state); +static void BM_AssetManagerGetResourceOld(benchmark::State& state) { + GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, + basic::R::integer::number1, state); } -BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, number1, basic::R::integer::number1); -BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, deep_ref, basic::R::integer::deep_ref); +BENCHMARK(BM_AssetManagerGetResourceOld); static void BM_AssetManagerGetLibraryResource(benchmark::State& state) { GetResourceBenchmark( @@ -197,7 +196,7 @@ BENCHMARK(BM_AssetManagerGetResourceLocales); static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) { AssetManager assets; if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/, - true /*isSystemAssets*/)) { + false /*isSystemAssets*/)) { state.SkipWithError("Failed to load assets"); return; } @@ -212,44 +211,4 @@ static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerGetResourceLocalesOld); -static void BM_AssetManagerSetConfigurationFramework(benchmark::State& state) { - std::unique_ptr apk = ApkAssets::Load(kFrameworkPath); - if (apk == nullptr) { - state.SkipWithError("Failed to load assets"); - return; - } - - AssetManager2 assets; - assets.SetApkAssets({apk.get()}); - - ResTable_config config; - memset(&config, 0, sizeof(config)); - - while (state.KeepRunning()) { - config.sdkVersion = ~config.sdkVersion; - assets.SetConfiguration(config); - } -} -BENCHMARK(BM_AssetManagerSetConfigurationFramework); - -static void BM_AssetManagerSetConfigurationFrameworkOld(benchmark::State& state) { - AssetManager assets; - if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/, - true /*isSystemAssets*/)) { - state.SkipWithError("Failed to load assets"); - return; - } - - const ResTable& table = assets.getResources(true); - - ResTable_config config; - memset(&config, 0, sizeof(config)); - - while (state.KeepRunning()) { - config.sdkVersion = ~config.sdkVersion; - assets.setConfiguration(config); - } -} -BENCHMARK(BM_AssetManagerSetConfigurationFrameworkOld); - } // namespace android diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp deleted file mode 100644 index fa300c50218aa..0000000000000 --- a/libs/androidfw/tests/AttributeResolution_bench.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "benchmark/benchmark.h" - -//#include "android-base/stringprintf.h" -#include "androidfw/ApkAssets.h" -#include "androidfw/AssetManager.h" -#include "androidfw/AssetManager2.h" -#include "androidfw/AttributeResolution.h" -#include "androidfw/ResourceTypes.h" - -#include "BenchmarkHelpers.h" -#include "data/basic/R.h" -#include "data/styles/R.h" - -namespace app = com::android::app; -namespace basic = com::android::basic; - -namespace android { - -constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk"; -constexpr const static uint32_t Theme_Material_Light = 0x01030237u; - -static void BM_ApplyStyle(benchmark::State& state) { - std::unique_ptr styles_apk = - ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); - if (styles_apk == nullptr) { - state.SkipWithError("failed to load assets"); - return; - } - - AssetManager2 assetmanager; - assetmanager.SetApkAssets({styles_apk.get()}); - - std::unique_ptr asset = - assetmanager.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); - if (asset == nullptr) { - state.SkipWithError("failed to load layout"); - return; - } - - ResXMLTree xml_tree; - if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) { - state.SkipWithError("corrupt xml layout"); - return; - } - - // Skip to the first tag. - while (xml_tree.next() != ResXMLParser::START_TAG) { - } - - std::unique_ptr theme = assetmanager.NewTheme(); - theme->ApplyStyle(app::R::style::StyleTwo); - - std::array attrs{{app::R::attr::attr_one, app::R::attr::attr_two, - app::R::attr::attr_three, app::R::attr::attr_four, - app::R::attr::attr_five, app::R::attr::attr_empty}}; - std::array values; - std::array indices; - - while (state.KeepRunning()) { - ApplyStyle(theme.get(), &xml_tree, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), - attrs.size(), values.data(), indices.data()); - } -} -BENCHMARK(BM_ApplyStyle); - -static void BM_ApplyStyleFramework(benchmark::State& state) { - std::unique_ptr framework_apk = ApkAssets::Load(kFrameworkPath); - if (framework_apk == nullptr) { - state.SkipWithError("failed to load framework assets"); - return; - } - - std::unique_ptr basic_apk = - ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - if (basic_apk == nullptr) { - state.SkipWithError("failed to load assets"); - return; - } - - AssetManager2 assetmanager; - assetmanager.SetApkAssets({framework_apk.get(), basic_apk.get()}); - - ResTable_config device_config; - memset(&device_config, 0, sizeof(device_config)); - device_config.language[0] = 'e'; - device_config.language[1] = 'n'; - device_config.country[0] = 'U'; - device_config.country[1] = 'S'; - device_config.orientation = ResTable_config::ORIENTATION_PORT; - device_config.smallestScreenWidthDp = 700; - device_config.screenWidthDp = 700; - device_config.screenHeightDp = 1024; - device_config.sdkVersion = 27; - - Res_value value; - ResTable_config config; - uint32_t flags = 0u; - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::layout::layoutt, false /*may_be_bag*/, - 0u /*density_override*/, &value, &config, &flags); - if (cookie == kInvalidCookie) { - state.SkipWithError("failed to find R.layout.layout"); - return; - } - - size_t len = 0u; - const char* layout_path = - assetmanager.GetStringPoolForCookie(cookie)->string8At(value.data, &len); - if (layout_path == nullptr || len == 0u) { - state.SkipWithError("failed to lookup layout path"); - return; - } - - std::unique_ptr asset = assetmanager.OpenNonAsset( - StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER); - if (asset == nullptr) { - state.SkipWithError("failed to load layout"); - return; - } - - ResXMLTree xml_tree; - if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) { - state.SkipWithError("corrupt xml layout"); - return; - } - - // Skip to the first tag. - while (xml_tree.next() != ResXMLParser::START_TAG) { - } - - std::unique_ptr theme = assetmanager.NewTheme(); - theme->ApplyStyle(Theme_Material_Light); - - std::array attrs{ - {0x0101000e, 0x01010034, 0x01010095, 0x01010096, 0x01010097, 0x01010098, 0x01010099, - 0x0101009a, 0x0101009b, 0x010100ab, 0x010100af, 0x010100b0, 0x010100b1, 0x0101011f, - 0x01010120, 0x0101013f, 0x01010140, 0x0101014e, 0x0101014f, 0x01010150, 0x01010151, - 0x01010152, 0x01010153, 0x01010154, 0x01010155, 0x01010156, 0x01010157, 0x01010158, - 0x01010159, 0x0101015a, 0x0101015b, 0x0101015c, 0x0101015d, 0x0101015e, 0x0101015f, - 0x01010160, 0x01010161, 0x01010162, 0x01010163, 0x01010164, 0x01010165, 0x01010166, - 0x01010167, 0x01010168, 0x01010169, 0x0101016a, 0x0101016b, 0x0101016c, 0x0101016d, - 0x0101016e, 0x0101016f, 0x01010170, 0x01010171, 0x01010217, 0x01010218, 0x0101021d, - 0x01010220, 0x01010223, 0x01010224, 0x01010264, 0x01010265, 0x01010266, 0x010102c5, - 0x010102c6, 0x010102c7, 0x01010314, 0x01010315, 0x01010316, 0x0101035e, 0x0101035f, - 0x01010362, 0x01010374, 0x0101038c, 0x01010392, 0x01010393, 0x010103ac, 0x0101045d, - 0x010104b6, 0x010104b7, 0x010104d6, 0x010104d7, 0x010104dd, 0x010104de, 0x010104df, - 0x01010535, 0x01010536, 0x01010537, 0x01010538, 0x01010546, 0x01010567, 0x011100c9, - 0x011100ca}}; - - std::array values; - std::array indices; - while (state.KeepRunning()) { - ApplyStyle(theme.get(), &xml_tree, 0x01010084u /*def_style_attr*/, 0u /*def_style_res*/, - attrs.data(), attrs.size(), values.data(), indices.data()); - } -} -BENCHMARK(BM_ApplyStyleFramework); - -} // namespace android diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index faddfe599af48..a8abcb5df86c2 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -42,12 +42,10 @@ void GetResourceBenchmarkOld(const std::vector& paths, const ResTab Res_value value; ResTable_config selected_config; uint32_t flags; - uint32_t last_ref = 0u; while (state.KeepRunning()) { - ssize_t block = table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, - &selected_config); - table.resolveReference(&value, block, &last_ref, &flags, &selected_config); + table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, + &selected_config); } } @@ -74,12 +72,10 @@ void GetResourceBenchmark(const std::vector& paths, const ResTable_ Res_value value; ResTable_config selected_config; uint32_t flags; - uint32_t last_id = 0u; while (state.KeepRunning()) { - ApkAssetsCookie cookie = assetmanager.GetResource( - resid, false /* may_be_bag */, 0u /* density_override */, &value, &selected_config, &flags); - assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_id); + assetmanager.GetResource(resid, false /* may_be_bag */, 0u /* density_override */, &value, + &selected_config, &flags); } } diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h index b7e814fea079b..94a2a14ced878 100644 --- a/libs/androidfw/tests/data/basic/R.h +++ b/libs/androidfw/tests/data/basic/R.h @@ -34,7 +34,6 @@ struct R { struct layout { enum : uint32_t { main = 0x7f020000, - layoutt = 0x7f020001, }; }; @@ -56,7 +55,6 @@ struct R { number2 = 0x7f040001, ref1 = 0x7f040002, ref2 = 0x7f040003, - deep_ref = 0x7f040004, // From feature number3 = 0x80030000, diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk index 1733b6a1654645caf190982b31abe9255a554e35..18ef75e91dedc0c9fbb63bfc7b0b0a34b0782af7 100644 GIT binary patch delta 644 zcmY+C%}X0$5XR?yv%7BMHraegRD#Kt7A*wZhzIclEX9)wmQ6vWJ%l7s54Mn`kXsKu z^wLAx>9vPI1do0lJb4yM5BUd5PyHKu^qI{jsW1GPcb;c0c_{Os1Z30}*a1Fx z4eG!&E3H8)toO}@a#+D$+J literal 5260 zcmds5d010d7QYDz31Jb9K!9i?i(;z?L=ig?l2^hZED{5+Ow;Job(#ounl4FeF#2YsrkINN+gB?bLv92Ge>Sn5 z&npNKD#zvdEOuyjzw2M^o0sLY$n7WLzXya$JRgp{aH;NO>#gnFOP^>?$E=ukc1Y;D zZ#j=tqVh)TH@~>Kb;}P+_jrd-Su0MDsw|B>-q)=x@{OCN${*yl(!me2%D!}rsxDYk zct8?(T2xa1Bx{EJZ!?5fUHSHmJ(_SQJZ&iMS|$@WRNvOkWPe|=Qo{O_~B>@ZvvM~4f9 z&7Y3)u7mZ~f(N2|^oSGm&}$7NQq-C0CL>*-PZEiMo+X*P z)&@)#2zh-kY4gCqYk;1YAhB4&@Ol2MRhWtoMJ`r27Cz&Pw&>Oe-fdKlI5xSD_u!2O zB+CTp0}o`#-OmYiw!Dh6KTa*FZnzVZlNVgL@xf*1afwYS-+h&H(r$m^CfoSDDw_(D zwRJq%o18c)@`UR?*`R%%pN6b-b#?eUt4*tETFfEzI?8=!ku1 z$B*$tBzCtNLbjY*Qh0UkllOK$ykJWI?1LMwW22JhuG?LwS(!Y28ei$;J!{Z5-BYz& zbCJS%cWK;|TCemFmm6a*dlzI3DY;gZvnqSg5%+w_m9=9$+YUFzHHwQmuU4(U^UXU= zZK|J>t~SX3(XzdG!PBQg_tDM~m1kozcOE;xqb1(7vHsf?nQjeZw%$H;{^{Y=0pWoS z4@=jGY9sedgVCzTrKdKDf! za^k2ep-I(L)%16l3$>w-Je!39Hk)=Y&Rx3jqDa%fIQ1?6)kW_r zzHsii79`#^ab{cEz>a{;?`=C*s2u6w?OIr~^M1~z{g)EnD9Vv~j~}XkxBT&+J{lZ+ zro}}*w7y`{WUqxAn+|<6Vs*eD?j5-4W0&B6+ePp+#x2_;`%atc)|wOH6+cOy1$ke8 z{|@K7s?p!5uRCSbue;3I75@0R=}@1)wm^;mS}5hN{?Bf_0dG*<0m6;%}$#Y zllA6!D^EeRz{*Ozi7XZsJM)^JMU&Ve+ycKUyExDM3HH=(TlAM9@?cPI1oXVyJS?Uk zo}?dcm>=lprx-~kTA3Z0Q|(kE&SzG}T85$d_v0{<|t zIYdfMLn#1I1eHMy52ZIi0hk8Bg)arH1bBFK`M9S=5{ZP>$>Q80uC5kM>ru?0Wjew0X_imz-LDI(*ti!Qb5WCf10Sgum*2p ziw^ue6WRq7XCzvXwA2Vb)gu$eX`!7DqxJBIIxr(KDoAKs0oaO1Dn-EO(JU5lXCaON z7k~`l1Mma94F~~50;T{kU(pWC*?hoqzGn1>nE3O(4){(+oOeCJ_kIOsNT47|U=tTCYv=Hx~g!DAZ`Rb7nvl5s4Uf zhCf>fU<-1#Fp@2dVhe$6VKiF^Vhdwn5pl`dRINY51Q2_BU7ArlQ>$0%_3BK2!sVvV zhG=jd30@k&^$;0~6!5=-!;K@Wfnn;=$Jp?ygt_Jj2Ngr8ABY37cH{$tZFC(|K5t`e zMLQ8ZFEEK`wl#;Y!}`}{E9RaMW8LdYdOY~C;l-TFj=4RFfU3;ZxK-dB2 zX6C}NFEdx)UT5fPe9>IEj^5@Y_7oqF!+w=;KDJMB0LB&b0@sQFQZPU}@<3qP zRCEIruS6JI#ujs2i52D_+GZ|^l{Ls{+e9d_kAt~XK+zpg)jLuCgdj3{mO*z#??e8l zR$PY+fajtD5DicP<^b{l#eh=4SAZ%&EdbYuzX!PIj?*&mD1(L8x!7()%T|O($s)Rc zqpx{9{Nz&o`BXb~#h_$sTE?6}eF9oW-=Qp|CJ-)k<%yV)e--tPmgyM16ODuB%WhS7Fo z{BggU+sroO9%Jg_fmZ-tmCz2nk08-!73QHMJuk)6zN2{TgMLN(aL+OQ(5K0)y4PhN z>@Vu~9BjfZLqA@S!|y-)8#YG_1Qvm-P#X+dqhW+8EipZH4qUhmT8*zU!x)#WGsq0d z>82EoEI})?Xcmd0!#UInnU6~Bk1Mz>E$IcHn7S)qf7)Pj!K|boxQq1;FyG7Yh1XpL z`|Xa`nJzkGp~CWuNOvvlN9XfPDWz&O#Ief|(87MibXUWEJX$*B2pmaWYM#FkdsE=i zwG`mS!yfEX(9>g#(MJu1uv@+o*$V5-`dDwq?qj>B3~MwbSPuyl9nQy%2RlGWJRw;o HQ@Qvb>2W7g diff --git a/libs/androidfw/tests/data/basic/res/layout/layout.xml b/libs/androidfw/tests/data/basic/res/layout/layout.xml deleted file mode 100644 index 045ede454bca3..0000000000000 --- a/libs/androidfw/tests/data/basic/res/layout/layout.xml +++ /dev/null @@ -1,25 +0,0 @@ - - -