diff options
190 files changed, 4144 insertions, 1195 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index 20f0ba86c00f..3b4a0be6cd13 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -384,16 +384,16 @@ package android.app { public final class StatsManager { method public void addConfig(long, byte[]) throws android.app.StatsManager.StatsUnavailableException; - method public boolean addConfiguration(long, byte[]); - method public byte[] getData(long); - method public byte[] getMetadata(); + method public deprecated boolean addConfiguration(long, byte[]); + method public deprecated byte[] getData(long); + method public deprecated byte[] getMetadata(); method public byte[] getReports(long) throws android.app.StatsManager.StatsUnavailableException; method public byte[] getStatsMetadata() throws android.app.StatsManager.StatsUnavailableException; method public void removeConfig(long) throws android.app.StatsManager.StatsUnavailableException; - method public boolean removeConfiguration(long); + method public deprecated boolean removeConfiguration(long); method public void setBroadcastSubscriber(android.app.PendingIntent, long, long) throws android.app.StatsManager.StatsUnavailableException; - method public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent); - method public boolean setDataFetchOperation(long, android.app.PendingIntent); + method public deprecated boolean setBroadcastSubscriber(long, long, android.app.PendingIntent); + method public deprecated boolean setDataFetchOperation(long, android.app.PendingIntent); method public void setFetchReportsOperation(android.app.PendingIntent, long) throws android.app.StatsManager.StatsUnavailableException; field public static final java.lang.String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED"; field public static final java.lang.String EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES = "android.app.extra.STATS_BROADCAST_SUBSCRIBER_COOKIES"; diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java index 2fc580805a62..c6d2bc891b18 100644 --- a/cmds/media/src/com/android/commands/media/Media.java +++ b/cmds/media/src/com/android/commands/media/Media.java @@ -171,7 +171,6 @@ public class Media extends BaseCommand { showError("Error: unknown dispatch code '" + cmd + "'"); return; } - final long now = SystemClock.uptimeMillis(); sendMediaKey(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keycode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD)); @@ -189,7 +188,6 @@ public class Media extends BaseCommand { @Override public void onSessionDestroyed() { System.out.println("onSessionDestroyed. Enter q to quit."); - } @Override @@ -246,7 +244,7 @@ public class Media extends BaseCommand { @Override protected void onLooperPrepared() { try { - mController.registerCallbackListener(ControllerMonitor.this); + mController.registerCallbackListener(PACKAGE_NAME, ControllerMonitor.this); } catch (RemoteException e) { System.out.println("Error registering monitor callback"); } @@ -266,13 +264,13 @@ public class Media extends BaseCommand { } else if ("q".equals(line) || "quit".equals(line)) { break; } else if ("play".equals(line)) { - mController.play(PACKAGE_NAME); + dispatchKeyCode(KeyEvent.KEYCODE_MEDIA_PLAY); } else if ("pause".equals(line)) { - mController.pause(PACKAGE_NAME); + dispatchKeyCode(KeyEvent.KEYCODE_MEDIA_PAUSE); } else if ("next".equals(line)) { - mController.next(PACKAGE_NAME); + dispatchKeyCode(KeyEvent.KEYCODE_MEDIA_NEXT); } else if ("previous".equals(line)) { - mController.previous(PACKAGE_NAME); + dispatchKeyCode(KeyEvent.KEYCODE_MEDIA_PREVIOUS); } else { System.out.println("Invalid command: " + line); } @@ -295,6 +293,20 @@ public class Media extends BaseCommand { } } } + + private void dispatchKeyCode(int keyCode) { + final long now = SystemClock.uptimeMillis(); + KeyEvent down = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0, 0, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD); + KeyEvent up = new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0, 0, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD); + try { + mController.sendMediaButton(PACKAGE_NAME, null, false, down); + mController.sendMediaButton(PACKAGE_NAME, null, false, up); + } catch (RemoteException e) { + System.out.println("Failed to dispatch " + keyCode); + } + } } private void runListSessions() { diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 96f04ac988c0..d278b551d593 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -352,6 +352,12 @@ Landroid/app/ApplicationPackageManager;->getPackageSizeInfoAsUser(Ljava/lang/Str Landroid/app/ApplicationPackageManager;->mPM:Landroid/content/pm/IPackageManager; Landroid/app/ApplicationPackageManager;->shouldShowRequestPermissionRationale(Ljava/lang/String;)Z Landroid/app/AppOpsManager$OpEntry;->getDuration()I +Landroid/app/AppOpsManager$OpEntry;->getLastAccessBackgroundTime()J +Landroid/app/AppOpsManager$OpEntry;->getLastAccessForegroundTime()J +Landroid/app/AppOpsManager$OpEntry;->getLastAccessTime()J +Landroid/app/AppOpsManager$OpEntry;->getLastRejectBackgroundTime()J +Landroid/app/AppOpsManager$OpEntry;->getLastRejectForegroundTime()J +Landroid/app/AppOpsManager$OpEntry;->getLastRejectTime()J Landroid/app/AppOpsManager$OpEntry;->getMode()I Landroid/app/AppOpsManager$OpEntry;->getRejectTime()J Landroid/app/AppOpsManager$PackageOps;-><init>(Ljava/lang/String;ILjava/util/List;)V @@ -359,6 +365,7 @@ Landroid/app/AppOpsManager$PackageOps;->CREATOR:Landroid/os/Parcelable$Creator; Landroid/app/AppOpsManager;->checkOp(IILjava/lang/String;)I Landroid/app/AppOpsManager;->checkOpNoThrow(IILjava/lang/String;)I Landroid/app/AppOpsManager;->getOpsForPackage(ILjava/lang/String;[I)Ljava/util/List; +Landroid/app/AppOpsManager;->MODE_FOREGROUND:I Landroid/app/AppOpsManager;->mService:Lcom/android/internal/app/IAppOpsService; Landroid/app/AppOpsManager;->noteOp(I)I Landroid/app/AppOpsManager;->noteOp(IILjava/lang/String;)I @@ -448,7 +455,10 @@ Landroid/app/AppOpsManager;->permissionToOpCode(Ljava/lang/String;)I Landroid/app/AppOpsManager;->resetAllModes()V Landroid/app/AppOpsManager;->setRestriction(III[Ljava/lang/String;)V Landroid/app/AppOpsManager;->sOpPerms:[Ljava/lang/String; +Landroid/app/AppOpsManager;->startWatchingMode(Ljava/lang/String;Ljava/lang/String;ILandroid/app/AppOpsManager$OnOpChangedListener;)V Landroid/app/AppOpsManager;->strOpToOp(Ljava/lang/String;)I +Landroid/app/AppOpsManager;->unsafeCheckOpRaw(Ljava/lang/String;ILjava/lang/String;)I +Landroid/app/AppOpsManager;->WATCH_FOREGROUND_CHANGES:I Landroid/app/AppOpsManager;->_NUM_OP:I Landroid/app/assist/AssistContent;-><init>(Landroid/os/Parcel;)V Landroid/app/assist/AssistContent;->mClipData:Landroid/content/ClipData; @@ -855,7 +865,9 @@ Landroid/app/PendingIntent;->setOnMarshaledListener(Landroid/app/PendingIntent$O Landroid/app/PictureInPictureArgs;-><init>()V Landroid/app/PictureInPictureArgs;->setActions(Ljava/util/List;)V Landroid/app/PictureInPictureArgs;->setAspectRatio(F)V +Landroid/app/PictureInPictureParams;->getActions()Ljava/util/List; Landroid/app/PictureInPictureParams;->getAspectRatio()F +Landroid/app/PictureInPictureParams;->getSourceRectHint()Landroid/graphics/Rect; Landroid/app/Presentation;->createPresentationContext(Landroid/content/Context;Landroid/view/Display;I)Landroid/content/Context; Landroid/app/ProgressDialog;->mMessageView:Landroid/widget/TextView; Landroid/app/ProgressDialog;->mProgress:Landroid/widget/ProgressBar; @@ -1029,6 +1041,7 @@ Landroid/bluetooth/BluetoothA2dp;->setCodecConfigPreference(Landroid/bluetooth/B Landroid/bluetooth/BluetoothA2dp;->setOptionalCodecsEnabled(Landroid/bluetooth/BluetoothDevice;I)V Landroid/bluetooth/BluetoothA2dp;->stateToString(I)Ljava/lang/String; Landroid/bluetooth/BluetoothA2dp;->supportsOptionalCodecs(Landroid/bluetooth/BluetoothDevice;)I +Landroid/bluetooth/BluetoothA2dpSink;->disconnect(Landroid/bluetooth/BluetoothDevice;)Z Landroid/bluetooth/BluetoothAdapter;->disable(Z)Z Landroid/bluetooth/BluetoothAdapter;->factoryReset()Z Landroid/bluetooth/BluetoothAdapter;->getBluetoothManager()Landroid/bluetooth/IBluetoothManager; @@ -1104,6 +1117,7 @@ Landroid/bluetooth/BluetoothDevice;->EXTRA_REASON:Ljava/lang/String; Landroid/bluetooth/BluetoothDevice;->EXTRA_SDP_SEARCH_STATUS:Ljava/lang/String; Landroid/bluetooth/BluetoothDevice;->getAlias()Ljava/lang/String; Landroid/bluetooth/BluetoothDevice;->getAliasName()Ljava/lang/String; +Landroid/bluetooth/BluetoothDevice;->getBatteryLevel()I Landroid/bluetooth/BluetoothDevice;->getMessageAccessPermission()I Landroid/bluetooth/BluetoothDevice;->getPhonebookAccessPermission()I Landroid/bluetooth/BluetoothDevice;->getService()Landroid/bluetooth/IBluetooth; @@ -1164,6 +1178,7 @@ Landroid/bluetooth/BluetoothHeadsetClientCall;->isOutgoing()Z Landroid/bluetooth/BluetoothHearingAid;->ACTION_ACTIVE_DEVICE_CHANGED:Ljava/lang/String; Landroid/bluetooth/BluetoothHearingAid;->getActiveDevices()Ljava/util/List; Landroid/bluetooth/BluetoothHearingAid;->setActiveDevice(Landroid/bluetooth/BluetoothDevice;)Z +Landroid/bluetooth/BluetoothMap;->disconnect(Landroid/bluetooth/BluetoothDevice;)Z Landroid/bluetooth/BluetoothMapClient;->sendMessage(Landroid/bluetooth/BluetoothDevice;[Landroid/net/Uri;Ljava/lang/String;Landroid/app/PendingIntent;Landroid/app/PendingIntent;)Z Landroid/bluetooth/BluetoothPan;-><init>(Landroid/content/Context;Landroid/bluetooth/BluetoothProfile$ServiceListener;)V Landroid/bluetooth/BluetoothPan;->close()V @@ -1175,11 +1190,13 @@ Landroid/bluetooth/BluetoothPan;->isTetheringOn()Z Landroid/bluetooth/BluetoothPan;->isValidDevice(Landroid/bluetooth/BluetoothDevice;)Z Landroid/bluetooth/BluetoothPan;->log(Ljava/lang/String;)V Landroid/bluetooth/BluetoothPan;->setBluetoothTethering(Z)V +Landroid/bluetooth/BluetoothPbap;->disconnect(Landroid/bluetooth/BluetoothDevice;)Z Landroid/bluetooth/BluetoothProfile;->A2DP_SINK:I Landroid/bluetooth/BluetoothProfile;->AVRCP_CONTROLLER:I Landroid/bluetooth/BluetoothProfile;->PAN:I Landroid/bluetooth/BluetoothProfile;->PRIORITY_AUTO_CONNECT:I Landroid/bluetooth/BluetoothProfile;->PRIORITY_UNDEFINED:I +Landroid/bluetooth/BluetoothSap;->disconnect(Landroid/bluetooth/BluetoothDevice;)Z Landroid/bluetooth/BluetoothServerSocket;->mSocket:Landroid/bluetooth/BluetoothSocket; Landroid/bluetooth/BluetoothSocket;->EADDRINUSE:I Landroid/bluetooth/BluetoothSocket;->flush()V @@ -1311,6 +1328,7 @@ Landroid/content/Context;->canStartActivityForResult()Z Landroid/content/Context;->checkPermission(Ljava/lang/String;IILandroid/os/IBinder;)I Landroid/content/Context;->COUNTRY_DETECTOR:Ljava/lang/String; Landroid/content/Context;->createApplicationContext(Landroid/content/pm/ApplicationInfo;I)Landroid/content/Context; +Landroid/content/Context;->ETHERNET_SERVICE:Ljava/lang/String; Landroid/content/Context;->getBasePackageName()Ljava/lang/String; Landroid/content/Context;->getDisplay()Landroid/view/Display; Landroid/content/Context;->getSharedPrefsFile(Ljava/lang/String;)Ljava/io/File; @@ -1389,6 +1407,7 @@ Landroid/content/ISyncAdapter$Stub$Proxy;->mRemote:Landroid/os/IBinder; Landroid/content/ISyncAdapter$Stub;-><init>()V Landroid/content/ISyncAdapter$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/ISyncAdapter; Landroid/content/ISyncAdapter;->cancelSync(Landroid/content/ISyncContext;)V +Landroid/content/ISyncAdapter;->onUnsyncableAccount(Landroid/content/ISyncAdapterUnsyncableAccountCallback;)V Landroid/content/ISyncAdapter;->startSync(Landroid/content/ISyncContext;Ljava/lang/String;Landroid/accounts/Account;Landroid/os/Bundle;)V Landroid/content/ISyncContext$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/content/ISyncContext$Stub$Proxy;->mRemote:Landroid/os/IBinder; @@ -1434,6 +1453,8 @@ Landroid/content/pm/ApplicationInfo;->scanSourceDir:Ljava/lang/String; Landroid/content/pm/ApplicationInfo;->secondaryCpuAbi:Ljava/lang/String; Landroid/content/pm/ApplicationInfo;->secondaryNativeLibraryDir:Ljava/lang/String; Landroid/content/pm/ApplicationInfo;->versionCode:I +Landroid/content/pm/BaseParceledListSlice;->getList()Ljava/util/List; +Landroid/content/pm/BaseParceledListSlice;->writeParcelableCreator(Ljava/lang/Object;Landroid/os/Parcel;)V Landroid/content/pm/ComponentInfo;->getComponentName()Landroid/content/ComponentName; Landroid/content/pm/IPackageDataObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder; @@ -1653,6 +1674,7 @@ Landroid/content/pm/PackageParser$Package;->mPreferredOrder:I Landroid/content/pm/PackageParser$Package;->mSharedUserId:Ljava/lang/String; Landroid/content/pm/PackageParser$Package;->mSharedUserLabel:I Landroid/content/pm/PackageParser$Package;->mSigningDetails:Landroid/content/pm/PackageParser$SigningDetails; +Landroid/content/pm/PackageParser$Package;->mUpgradeKeySets:Landroid/util/ArraySet; Landroid/content/pm/PackageParser$Package;->mVersionCode:I Landroid/content/pm/PackageParser$Package;->mVersionName:Ljava/lang/String; Landroid/content/pm/PackageParser$Package;->packageName:Ljava/lang/String; @@ -1706,6 +1728,8 @@ Landroid/content/pm/PackageParser;->setSeparateProcesses([Ljava/lang/String;)V Landroid/content/pm/PackageStats;->userHandle:I Landroid/content/pm/PackageUserState;-><init>()V Landroid/content/pm/ParceledListSlice;-><init>(Ljava/util/List;)V +Landroid/content/pm/ParceledListSlice;->CREATOR:Landroid/os/Parcelable$ClassLoaderCreator; +Landroid/content/pm/ParceledListSlice;->writeParcelableCreator(Landroid/os/Parcelable;Landroid/os/Parcel;)V Landroid/content/pm/PermissionInfo;->protectionToString(I)Ljava/lang/String; Landroid/content/pm/RegisteredServicesCache$ServiceInfo;->componentName:Landroid/content/ComponentName; Landroid/content/pm/RegisteredServicesCache$ServiceInfo;->type:Ljava/lang/Object; @@ -2088,6 +2112,8 @@ Landroid/graphics/Canvas;->getNativeCanvasWrapper()J Landroid/graphics/Canvas;->mBitmap:Landroid/graphics/Bitmap; Landroid/graphics/Canvas;->release()V Landroid/graphics/Canvas;->setScreenDensity(I)V +Landroid/graphics/CanvasProperty;->createFloat(F)Landroid/graphics/CanvasProperty; +Landroid/graphics/CanvasProperty;->createPaint(Landroid/graphics/Paint;)Landroid/graphics/CanvasProperty; Landroid/graphics/ColorMatrixColorFilter;->mMatrix:Landroid/graphics/ColorMatrix; Landroid/graphics/ColorMatrixColorFilter;->setColorMatrix(Landroid/graphics/ColorMatrix;)V Landroid/graphics/drawable/AnimatedImageDrawable;->onAnimationEnd()V @@ -2197,6 +2223,7 @@ Landroid/graphics/drawable/VectorDrawable;->getTargetByName(Ljava/lang/String;)L Landroid/graphics/drawable/VectorDrawable;->mTintFilter:Landroid/graphics/PorterDuffColorFilter; Landroid/graphics/drawable/VectorDrawable;->setAllowCaching(Z)V Landroid/graphics/FontFamily;-><init>()V +Landroid/graphics/FontFamily;-><init>([Ljava/lang/String;I)V Landroid/graphics/FontFamily;->abortCreation()V Landroid/graphics/FontFamily;->addFontFromAssetManager(Landroid/content/res/AssetManager;Ljava/lang/String;IZIII[Landroid/graphics/fonts/FontVariationAxis;)Z Landroid/graphics/FontFamily;->addFontFromBuffer(Ljava/nio/ByteBuffer;I[Landroid/graphics/fonts/FontVariationAxis;II)Z @@ -2216,6 +2243,8 @@ Landroid/graphics/Insets;->of(IIII)Landroid/graphics/Insets; Landroid/graphics/Insets;->of(Landroid/graphics/Rect;)Landroid/graphics/Insets; Landroid/graphics/Insets;->right:I Landroid/graphics/Insets;->top:I +Landroid/graphics/LightingColorFilter;->setColorAdd(I)V +Landroid/graphics/LightingColorFilter;->setColorMultiply(I)V Landroid/graphics/LinearGradient;->mColor0:I Landroid/graphics/LinearGradient;->mColor1:I Landroid/graphics/LinearGradient;->mColors:[I @@ -2281,6 +2310,7 @@ Landroid/graphics/SweepGradient;->mPositions:[F Landroid/graphics/TableMaskFilter;->CreateClipTable(II)Landroid/graphics/TableMaskFilter; Landroid/graphics/TemporaryBuffer;->obtain(I)[C Landroid/graphics/TemporaryBuffer;->recycle([C)V +Landroid/graphics/Typeface;-><init>(J)V Landroid/graphics/Typeface;->createFromFamilies([Landroid/graphics/FontFamily;)Landroid/graphics/Typeface; Landroid/graphics/Typeface;->createFromFamiliesWithDefault([Landroid/graphics/FontFamily;II)Landroid/graphics/Typeface; Landroid/graphics/Typeface;->createFromFamiliesWithDefault([Landroid/graphics/FontFamily;Ljava/lang/String;II)Landroid/graphics/Typeface; @@ -2290,6 +2320,7 @@ Landroid/graphics/Typeface;->nativeCreateWeightAlias(JI)J Landroid/graphics/Typeface;->native_instance:J Landroid/graphics/Typeface;->sDefaults:[Landroid/graphics/Typeface; Landroid/graphics/Typeface;->setDefault(Landroid/graphics/Typeface;)V +Landroid/graphics/Typeface;->sSystemFallbackMap:Ljava/util/Map; Landroid/graphics/Typeface;->sSystemFontMap:Ljava/util/Map; Landroid/graphics/Xfermode;->porterDuffMode:I Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_GOOD:I @@ -2413,6 +2444,7 @@ Landroid/hardware/Camera;->native_setup(Ljava/lang/Object;IILjava/lang/String;)I Landroid/hardware/Camera;->openLegacy(II)Landroid/hardware/Camera; Landroid/hardware/Camera;->postEventFromNative(Ljava/lang/Object;IIILjava/lang/Object;)V Landroid/hardware/Camera;->previewEnabled()Z +Landroid/hardware/Camera;->setPreviewSurface(Landroid/view/Surface;)V Landroid/hardware/display/DisplayManager;->ACTION_WIFI_DISPLAY_STATUS_CHANGED:Ljava/lang/String; Landroid/hardware/display/DisplayManager;->connectWifiDisplay(Ljava/lang/String;)V Landroid/hardware/display/DisplayManager;->disconnectWifiDisplay()V @@ -3241,6 +3273,14 @@ Landroid/net/ConnectivityManager;->TYPE_NONE:I Landroid/net/ConnectivityManager;->TYPE_PROXY:I Landroid/net/ConnectivityManager;->TYPE_WIFI_P2P:I Landroid/net/ConnectivityManager;->unregisterNetworkFactory(Landroid/os/Messenger;)V +Landroid/net/EthernetManager$Listener;->onAvailabilityChanged(Ljava/lang/String;Z)V +Landroid/net/EthernetManager;->addListener(Landroid/net/EthernetManager$Listener;)V +Landroid/net/EthernetManager;->getAvailableInterfaces()[Ljava/lang/String; +Landroid/net/EthernetManager;->getConfiguration(Ljava/lang/String;)Landroid/net/IpConfiguration; +Landroid/net/EthernetManager;->isAvailable()Z +Landroid/net/EthernetManager;->isAvailable(Ljava/lang/String;)Z +Landroid/net/EthernetManager;->removeListener(Landroid/net/EthernetManager$Listener;)V +Landroid/net/EthernetManager;->setConfiguration(Ljava/lang/String;Landroid/net/IpConfiguration;)V Landroid/net/http/SslCertificate;->getDigest(Ljava/security/cert/X509Certificate;Ljava/lang/String;)Ljava/lang/String; Landroid/net/http/SslCertificate;->getSerialNumber(Ljava/security/cert/X509Certificate;)Ljava/lang/String; Landroid/net/http/SslCertificate;->inflateCertificateView(Landroid/content/Context;)Landroid/view/View; @@ -3855,6 +3895,7 @@ Landroid/os/Environment;->buildExternalStorageAppDataDirs(Ljava/lang/String;)[Lj Landroid/os/Environment;->buildExternalStorageAppFilesDirs(Ljava/lang/String;)[Ljava/io/File; Landroid/os/Environment;->buildExternalStorageAppMediaDirs(Ljava/lang/String;)[Ljava/io/File; Landroid/os/Environment;->buildExternalStorageAppObbDirs(Ljava/lang/String;)[Ljava/io/File; +Landroid/os/Environment;->getDataSystemDirectory()Ljava/io/File; Landroid/os/Environment;->getLegacyExternalStorageObbDirectory()Ljava/io/File; Landroid/os/Environment;->getOemDirectory()Ljava/io/File; Landroid/os/Environment;->getStorageDirectory()Ljava/io/File; @@ -4129,6 +4170,7 @@ Landroid/os/storage/StorageManager;->getVolumePaths()[Ljava/lang/String; Landroid/os/storage/StorageManager;->getVolumes()Ljava/util/List; Landroid/os/storage/StorageManager;->getVolumeState(Ljava/lang/String;)Ljava/lang/String; Landroid/os/storage/StorageManager;->isFileEncryptedNativeOnly()Z +Landroid/os/storage/StorageManager;->isUsbMassStorageConnected()Z Landroid/os/storage/StorageManager;->isUsbMassStorageEnabled()Z Landroid/os/storage/StorageManager;->partitionPublic(Ljava/lang/String;)V Landroid/os/storage/StorageManager;->unmount(Ljava/lang/String;)V @@ -4139,7 +4181,10 @@ Landroid/os/storage/StorageVolume;->getOwner()Landroid/os/UserHandle; Landroid/os/storage/StorageVolume;->getPath()Ljava/lang/String; Landroid/os/storage/StorageVolume;->getPathFile()Ljava/io/File; Landroid/os/storage/StorageVolume;->getUserLabel()Ljava/lang/String; +Landroid/os/storage/StorageVolume;->mDescription:Ljava/lang/String; +Landroid/os/storage/StorageVolume;->mId:Ljava/lang/String; Landroid/os/storage/StorageVolume;->mPath:Ljava/io/File; +Landroid/os/storage/StorageVolume;->mPrimary:Z Landroid/os/storage/StorageVolume;->mRemovable:Z Landroid/os/storage/VolumeInfo;-><init>(Landroid/os/Parcel;)V Landroid/os/storage/VolumeInfo;->buildBrowseIntent()Landroid/content/Intent; @@ -4187,6 +4232,7 @@ Landroid/os/StrictMode;->incrementExpectedActivityCount(Ljava/lang/Class;)V Landroid/os/StrictMode;->onBinderStrictModePolicyChange(I)V Landroid/os/StrictMode;->onWebViewMethodCalledOnWrongThread(Ljava/lang/Throwable;)V Landroid/os/StrictMode;->sLastVmViolationTime:Ljava/util/HashMap; +Landroid/os/StrictMode;->sWindowManager:Landroid/util/Singleton; Landroid/os/StrictMode;->violationsBeingTimed:Ljava/lang/ThreadLocal; Landroid/os/SystemClock;->currentThreadTimeMicro()J Landroid/os/SystemClock;->currentTimeMicro()J @@ -4467,6 +4513,7 @@ Landroid/provider/Settings$Global;->PREFERRED_NETWORK_MODE:Ljava/lang/String; Landroid/provider/Settings$Global;->putStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;I)Z Landroid/provider/Settings$Global;->REQUIRE_PASSWORD_TO_DECRYPT:Ljava/lang/String; Landroid/provider/Settings$Global;->sNameValueCache:Landroid/provider/Settings$NameValueCache; +Landroid/provider/Settings$Global;->sProviderHolder:Landroid/provider/Settings$ContentProviderHolder; Landroid/provider/Settings$Global;->WEBVIEW_PROVIDER:Ljava/lang/String; Landroid/provider/Settings$Global;->WIFI_SAVED_STATE:Ljava/lang/String; Landroid/provider/Settings$Global;->WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED:Ljava/lang/String; @@ -4479,6 +4526,7 @@ Landroid/provider/Settings$Global;->ZEN_MODE_OFF:I Landroid/provider/Settings$NameValueCache;->mProviderHolder:Landroid/provider/Settings$ContentProviderHolder; Landroid/provider/Settings$Secure;->ACCESSIBILITY_AUTOCLICK_ENABLED:Ljava/lang/String; Landroid/provider/Settings$Secure;->ACCESSIBILITY_CAPTIONING_TYPEFACE:Ljava/lang/String; +Landroid/provider/Settings$Secure;->ACCESSIBILITY_DISPLAY_DALTONIZER:Ljava/lang/String; Landroid/provider/Settings$Secure;->ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:Ljava/lang/String; Landroid/provider/Settings$Secure;->ACCESSIBILITY_LARGE_POINTER_ICON:Ljava/lang/String; Landroid/provider/Settings$Secure;->ANR_SHOW_BACKGROUND:Ljava/lang/String; @@ -4526,6 +4574,7 @@ Landroid/provider/Settings$System;->HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY: Landroid/provider/Settings$System;->LOCKSCREEN_SOUNDS_ENABLED:Ljava/lang/String; Landroid/provider/Settings$System;->LOCK_SOUND:Ljava/lang/String; Landroid/provider/Settings$System;->MASTER_MONO:Ljava/lang/String; +Landroid/provider/Settings$System;->NOTIFICATION_LIGHT_PULSE:Ljava/lang/String; Landroid/provider/Settings$System;->POINTER_LOCATION:Ljava/lang/String; Landroid/provider/Settings$System;->POINTER_SPEED:Ljava/lang/String; Landroid/provider/Settings$System;->PRIVATE_SETTINGS:Ljava/util/Set; @@ -4536,6 +4585,7 @@ Landroid/provider/Settings$System;->SCREEN_AUTO_BRIGHTNESS_ADJ:Ljava/lang/String Landroid/provider/Settings$System;->SETTINGS_TO_BACKUP:[Ljava/lang/String; Landroid/provider/Settings$System;->SHOW_TOUCHES:Ljava/lang/String; Landroid/provider/Settings$System;->sNameValueCache:Landroid/provider/Settings$NameValueCache; +Landroid/provider/Settings$System;->sProviderHolder:Landroid/provider/Settings$ContentProviderHolder; Landroid/provider/Settings$System;->TTY_MODE:Ljava/lang/String; Landroid/provider/Settings$System;->UNLOCK_SOUND:Ljava/lang/String; Landroid/provider/Settings$System;->VIBRATE_IN_SILENT:Ljava/lang/String; @@ -4651,6 +4701,7 @@ Landroid/R$styleable;->ProgressBar_minWidth:I Landroid/R$styleable;->ProgressBar_progressDrawable:I Landroid/R$styleable;->RingtonePreference:[I Landroid/R$styleable;->ScrollView:[I +Landroid/R$styleable;->SearchView:[I Landroid/R$styleable;->SeekBar:[I Landroid/R$styleable;->SeekBar_thumb:I Landroid/R$styleable;->SeekBar_thumbOffset:I @@ -5201,6 +5252,7 @@ Landroid/telephony/CellSignalStrengthWcdma;->mSignalStrength:I Landroid/telephony/DisconnectCause;->toString(I)Ljava/lang/String; Landroid/telephony/euicc/EuiccInfo;->osVersion:Ljava/lang/String; Landroid/telephony/gsm/GsmCellLocation;->setPsc(I)V +Landroid/telephony/ims/ImsSsInfo;-><init>()V Landroid/telephony/NeighboringCellInfo;->mCid:I Landroid/telephony/NeighboringCellInfo;->mLac:I Landroid/telephony/NeighboringCellInfo;->mNetworkType:I @@ -5218,6 +5270,7 @@ Landroid/telephony/PhoneNumberUtils;->getUsernameFromUriNumber(Ljava/lang/String Landroid/telephony/PhoneNumberUtils;->isEmergencyNumber(Ljava/lang/String;Ljava/lang/String;)Z Landroid/telephony/PhoneNumberUtils;->isLocalEmergencyNumber(Landroid/content/Context;ILjava/lang/String;)Z Landroid/telephony/PhoneNumberUtils;->isNanp(Ljava/lang/String;)Z +Landroid/telephony/PhoneNumberUtils;->isPotentialLocalEmergencyNumber(Landroid/content/Context;Ljava/lang/String;)Z Landroid/telephony/PhoneNumberUtils;->isUriNumber(Ljava/lang/String;)Z Landroid/telephony/PhoneNumberUtils;->isVoiceMailNumber(Landroid/content/Context;ILjava/lang/String;)Z Landroid/telephony/PhoneNumberUtils;->MIN_MATCH:I @@ -5381,6 +5434,7 @@ Landroid/telephony/SubscriptionManager;->NAME_SOURCE_USER_INPUT:I Landroid/telephony/SubscriptionManager;->setDataRoaming(II)I Landroid/telephony/SubscriptionManager;->setDefaultDataSubId(I)V Landroid/telephony/SubscriptionManager;->setDefaultSmsSubId(I)V +Landroid/telephony/SubscriptionManager;->setDisplayNumber(Ljava/lang/String;I)I Landroid/telephony/TelephonyManager$MultiSimVariants;->TSTS:Landroid/telephony/TelephonyManager$MultiSimVariants; Landroid/telephony/TelephonyManager$MultiSimVariants;->UNKNOWN:Landroid/telephony/TelephonyManager$MultiSimVariants; Landroid/telephony/TelephonyManager$MultiSimVariants;->values()[Landroid/telephony/TelephonyManager$MultiSimVariants; @@ -5678,6 +5732,7 @@ Landroid/util/MathUtils;->abs(F)F Landroid/util/MathUtils;->constrain(FFF)F Landroid/util/MathUtils;->constrain(III)I Landroid/util/MathUtils;->lerp(FFF)F +Landroid/util/MathUtils;->max(II)F Landroid/util/NtpTrustedTime;->forceRefresh()Z Landroid/util/NtpTrustedTime;->getCachedNtpTime()J Landroid/util/NtpTrustedTime;->getCachedNtpTimeReference()J @@ -5727,6 +5782,7 @@ Landroid/view/accessibility/AccessibilityEvent;->mEventType:I Landroid/view/accessibility/AccessibilityInteractionClient;->clearCache()V Landroid/view/accessibility/AccessibilityInteractionClient;->getInstance()Landroid/view/accessibility/AccessibilityInteractionClient; Landroid/view/accessibility/AccessibilityInteractionClient;->setSameThreadMessage(Landroid/os/Message;)V +Landroid/view/accessibility/AccessibilityManager;->DALTONIZER_SIMULATE_MONOCHROMACY:I Landroid/view/accessibility/AccessibilityManager;->getInstance(Landroid/content/Context;)Landroid/view/accessibility/AccessibilityManager; Landroid/view/accessibility/AccessibilityManager;->isHighTextContrastEnabled()Z Landroid/view/accessibility/AccessibilityManager;->mAccessibilityStateChangeListeners:Landroid/util/ArrayMap; @@ -5764,6 +5820,7 @@ Landroid/view/AccessibilityIterators$AbstractTextSegmentIterator;->mText:Ljava/l Landroid/view/ActionProvider;->reset()V Landroid/view/ActionProvider;->setSubUiVisibilityListener(Landroid/view/ActionProvider$SubUiVisibilityListener;)V Landroid/view/animation/Animation;->detach()V +Landroid/view/animation/Animation;->getInvalidateRegion(IIIILandroid/graphics/RectF;Landroid/view/animation/Transformation;)V Landroid/view/animation/Animation;->initializeInvalidateRegion(IIII)V Landroid/view/animation/Animation;->mListener:Landroid/view/animation/Animation$AnimationListener; Landroid/view/animation/Animation;->mPreviousRegion:Landroid/graphics/RectF; @@ -5825,6 +5882,7 @@ Landroid/view/DisplayInfo;->logicalHeight:I Landroid/view/DisplayInfo;->logicalWidth:I Landroid/view/DisplayInfo;->rotation:I Landroid/view/DisplayListCanvas;->callDrawGLFunction2(J)V +Landroid/view/DisplayListCanvas;->drawCircle(Landroid/graphics/CanvasProperty;Landroid/graphics/CanvasProperty;Landroid/graphics/CanvasProperty;Landroid/graphics/CanvasProperty;)V Landroid/view/DisplayListCanvas;->drawGLFunctor2(JLjava/lang/Runnable;)V Landroid/view/DragEvent;->mClipData:Landroid/content/ClipData; Landroid/view/DragEvent;->mClipDescription:Landroid/content/ClipDescription; @@ -6031,9 +6089,13 @@ Landroid/view/RenderNode;->discardDisplayList()V Landroid/view/RenderNode;->offsetLeftAndRight(I)Z Landroid/view/RenderNode;->output()V Landroid/view/RenderNode;->setHasOverlappingRendering(Z)Z +Landroid/view/RenderNode;->setProjectBackwards(Z)Z Landroid/view/RenderNodeAnimator;-><init>(IF)V +Landroid/view/RenderNodeAnimator;-><init>(Landroid/graphics/CanvasProperty;F)V +Landroid/view/RenderNodeAnimator;-><init>(Landroid/graphics/CanvasProperty;IF)V Landroid/view/RenderNodeAnimator;->callOnFinished(Landroid/view/RenderNodeAnimator;)V Landroid/view/RenderNodeAnimator;->mapViewPropertyToRenderProperty(I)I +Landroid/view/RenderNodeAnimator;->setStartValue(F)V Landroid/view/RenderNodeAnimator;->setTarget(Landroid/view/View;)V Landroid/view/ScaleGestureDetector;->mListener:Landroid/view/ScaleGestureDetector$OnScaleGestureListener; Landroid/view/ScaleGestureDetector;->mMinSpan:I @@ -6125,6 +6187,10 @@ Landroid/view/TextureView;->mUpdateSurface:Z Landroid/view/TextureView;->nCreateNativeWindow(Landroid/graphics/SurfaceTexture;)V Landroid/view/TextureView;->nDestroyNativeWindow()V Landroid/view/TextureView;->onDetachedFromWindowInternal()V +Landroid/view/ThreadedRenderer;->addRenderNode(Landroid/view/RenderNode;Z)V +Landroid/view/ThreadedRenderer;->drawRenderNode(Landroid/view/RenderNode;)V +Landroid/view/ThreadedRenderer;->removeRenderNode(Landroid/view/RenderNode;)V +Landroid/view/ThreadedRenderer;->setContentDrawBounds(IIII)V Landroid/view/ThreadedRenderer;->setupDiskCache(Ljava/io/File;)V Landroid/view/TouchDelegate;->mDelegateTargeted:Z Landroid/view/VelocityTracker$Estimator;->confidence:F @@ -6204,10 +6270,12 @@ Landroid/view/View;->getInverseMatrix()Landroid/graphics/Matrix; Landroid/view/View;->getIterableTextForAccessibility()Ljava/lang/CharSequence; Landroid/view/View;->getIteratorForGranularity(I)Landroid/view/AccessibilityIterators$TextSegmentIterator; Landroid/view/View;->getListenerInfo()Landroid/view/View$ListenerInfo; +Landroid/view/View;->getLocationInSurface([I)V Landroid/view/View;->getLocationOnScreen()[I Landroid/view/View;->getRawTextAlignment()I Landroid/view/View;->getRawTextDirection()I Landroid/view/View;->getScrollCache()Landroid/view/View$ScrollabilityCache; +Landroid/view/View;->getThreadedRenderer()Landroid/view/ThreadedRenderer; Landroid/view/View;->getTransitionAlpha()F Landroid/view/View;->getVerticalScrollFactor()F Landroid/view/View;->getViewRootImpl()Landroid/view/ViewRootImpl; @@ -6277,8 +6345,11 @@ Landroid/view/View;->NAVIGATION_BAR_TRANSIENT:I Landroid/view/View;->notifySubtreeAccessibilityStateChangedIfNeeded()V Landroid/view/View;->notifyViewAccessibilityStateChangedIfNeeded(I)V Landroid/view/View;->onCloseSystemDialogs(Ljava/lang/String;)V +Landroid/view/View;->onDetachedFromWindowInternal()V +Landroid/view/View;->onDrawHorizontalScrollBar(Landroid/graphics/Canvas;Landroid/graphics/drawable/Drawable;IIII)V Landroid/view/View;->onDrawVerticalScrollBar(Landroid/graphics/Canvas;Landroid/graphics/drawable/Drawable;IIII)V Landroid/view/View;->onFocusLost()V +Landroid/view/View;->onInitializeAccessibilityEventInternal(Landroid/view/accessibility/AccessibilityEvent;)V Landroid/view/View;->performAccessibilityActionInternal(ILandroid/os/Bundle;)Z Landroid/view/View;->pointInView(FFF)Z Landroid/view/View;->recomputePadding()V @@ -6312,6 +6383,7 @@ Landroid/view/View;->toGlobalMotionEvent(Landroid/view/MotionEvent;)Z Landroid/view/View;->toLocalMotionEvent(Landroid/view/MotionEvent;)Z Landroid/view/View;->transformMatrixToGlobal(Landroid/graphics/Matrix;)V Landroid/view/View;->transformMatrixToLocal(Landroid/graphics/Matrix;)V +Landroid/view/View;->updateDisplayListIfDirty()Landroid/view/RenderNode; Landroid/view/ViewConfiguration;->getDeviceGlobalActionKeyTimeout()J Landroid/view/ViewConfiguration;->getDoubleTapMinTime()I Landroid/view/ViewConfiguration;->getDoubleTapSlop()I @@ -6340,6 +6412,7 @@ Landroid/view/ViewGroup;->dispatchDetachedFromWindow()V Landroid/view/ViewGroup;->dispatchGetDisplayList()V Landroid/view/ViewGroup;->dispatchViewAdded(Landroid/view/View;)V Landroid/view/ViewGroup;->dispatchViewRemoved(Landroid/view/View;)V +Landroid/view/ViewGroup;->encodeProperties(Landroid/view/ViewHierarchyEncoder;)V Landroid/view/ViewGroup;->FLAG_DISALLOW_INTERCEPT:I Landroid/view/ViewGroup;->FLAG_SUPPORT_STATIC_TRANSFORMATIONS:I Landroid/view/ViewGroup;->FLAG_USE_CHILD_DRAWING_ORDER:I @@ -6357,6 +6430,7 @@ Landroid/view/ViewGroup;->mOnHierarchyChangeListener:Landroid/view/ViewGroup$OnH Landroid/view/ViewGroup;->mPersistentDrawingCache:I Landroid/view/ViewGroup;->offsetChildrenTopAndBottom(I)V Landroid/view/ViewGroup;->onChildVisibilityChanged(Landroid/view/View;II)V +Landroid/view/ViewGroup;->onInitializeAccessibilityNodeInfoInternal(Landroid/view/accessibility/AccessibilityNodeInfo;)V Landroid/view/ViewGroup;->removeTransientView(Landroid/view/View;)V Landroid/view/ViewGroup;->resetResolvedDrawables()V Landroid/view/ViewGroup;->resetResolvedLayoutDirection()V @@ -6530,6 +6604,8 @@ Landroid/webkit/ConsoleMessage;->mSourceId:Ljava/lang/String; Landroid/webkit/IWebViewUpdateService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/webkit/IWebViewUpdateService$Stub$Proxy;->waitForAndGetProvider()Landroid/webkit/WebViewProviderResponse; Landroid/webkit/IWebViewUpdateService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/webkit/IWebViewUpdateService; +Landroid/webkit/IWebViewUpdateService;->getCurrentWebViewPackageName()Ljava/lang/String; +Landroid/webkit/IWebViewUpdateService;->getValidWebViewPackages()[Landroid/webkit/WebViewProviderInfo; Landroid/webkit/IWebViewUpdateService;->isFallbackPackage(Ljava/lang/String;)Z Landroid/webkit/JsResult;->mReceiver:Landroid/webkit/JsResult$ResultReceiver; Landroid/webkit/PluginData;-><init>(Ljava/io/InputStream;JLjava/util/Map;I)V @@ -6537,6 +6613,8 @@ Landroid/webkit/PluginData;->getContentLength()J Landroid/webkit/PluginData;->getHeaders()Ljava/util/Map; Landroid/webkit/PluginData;->getInputStream()Ljava/io/InputStream; Landroid/webkit/PluginData;->getStatusCode()I +Landroid/webkit/UrlInterceptHandler;->getPluginData(Ljava/lang/String;Ljava/util/Map;)Landroid/webkit/PluginData; +Landroid/webkit/UrlInterceptHandler;->service(Ljava/lang/String;Ljava/util/Map;)Landroid/webkit/CacheManager$CacheResult; Landroid/webkit/UrlInterceptRegistry;->getPluginData(Ljava/lang/String;Ljava/util/Map;)Landroid/webkit/PluginData; Landroid/webkit/UrlInterceptRegistry;->registerHandler(Landroid/webkit/UrlInterceptHandler;)Z Landroid/webkit/UrlInterceptRegistry;->setUrlInterceptDisabled(Z)V @@ -6695,6 +6773,7 @@ Landroid/widget/AutoCompleteTextView;->mPassThroughClickListener:Landroid/widget Landroid/widget/AutoCompleteTextView;->mPopup:Landroid/widget/ListPopupWindow; Landroid/widget/AutoCompleteTextView;->setDropDownAlwaysVisible(Z)V Landroid/widget/AutoCompleteTextView;->setDropDownAnimationStyle(I)V +Landroid/widget/AutoCompleteTextView;->setDropDownDismissedOnCompletion(Z)V Landroid/widget/AutoCompleteTextView;->setForceIgnoreOutsideTouch(Z)V Landroid/widget/AutoCompleteTextView;->showDropDownAfterLayout()V Landroid/widget/BaseAdapter;->mDataSetObservable:Landroid/database/DataSetObservable; @@ -6721,7 +6800,9 @@ Landroid/widget/Editor$InputContentType;->privateImeOptions:Ljava/lang/String; Landroid/widget/Editor;->invalidateTextDisplayList()V Landroid/widget/Editor;->mCreatedWithASelection:Z Landroid/widget/Editor;->mInsertionControllerEnabled:Z +Landroid/widget/Editor;->mSelectHandleCenter:Landroid/graphics/drawable/Drawable; Landroid/widget/Editor;->mSelectHandleLeft:Landroid/graphics/drawable/Drawable; +Landroid/widget/Editor;->mSelectHandleRight:Landroid/graphics/drawable/Drawable; Landroid/widget/Editor;->mSelectionControllerEnabled:Z Landroid/widget/Editor;->mShowCursor:J Landroid/widget/Editor;->mShowSoftInputOnFocus:Z @@ -6822,8 +6903,10 @@ Landroid/widget/LinearLayout;->mMaxDescent:[I Landroid/widget/LinearLayout;->mTotalLength:I Landroid/widget/LinearLayout;->mUseLargestChild:Z Landroid/widget/ListPopupWindow;->buildDropDown()I +Landroid/widget/ListPopupWindow;->isDropDownAlwaysVisible()Z Landroid/widget/ListPopupWindow;->mDropDownList:Landroid/widget/DropDownListView; Landroid/widget/ListPopupWindow;->mPopup:Landroid/widget/PopupWindow; +Landroid/widget/ListPopupWindow;->setDropDownAlwaysVisible(Z)V Landroid/widget/ListPopupWindow;->setForceIgnoreOutsideTouch(Z)V Landroid/widget/ListPopupWindow;->setListItemExpandMax(I)V Landroid/widget/ListView;->arrowScroll(I)Z @@ -7441,6 +7524,7 @@ Lcom/android/internal/os/BatteryStatsHelper;-><init>(Landroid/content/Context;Z) Lcom/android/internal/os/BatteryStatsHelper;-><init>(Landroid/content/Context;ZZ)V Lcom/android/internal/os/BatteryStatsHelper;->clearStats()V Lcom/android/internal/os/BatteryStatsHelper;->create(Landroid/os/Bundle;)V +Lcom/android/internal/os/BatteryStatsHelper;->dropFile(Landroid/content/Context;Ljava/lang/String;)V Lcom/android/internal/os/BatteryStatsHelper;->getBatteryBroadcast()Landroid/content/Intent; Lcom/android/internal/os/BatteryStatsHelper;->getMaxPower()D Lcom/android/internal/os/BatteryStatsHelper;->getStats()Landroid/os/BatteryStats; @@ -7565,6 +7649,8 @@ Lcom/android/internal/policy/IKeyguardService;->doKeyguardTimeout(Landroid/os/Bu Lcom/android/internal/policy/IKeyguardService;->setKeyguardEnabled(Z)V Lcom/android/internal/policy/IKeyguardStateCallback$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/policy/IKeyguardStateCallback; Lcom/android/internal/R$anim;->fade_in:I +Lcom/android/internal/R$array;->config_autoBrightnessLcdBacklightValues:I +Lcom/android/internal/R$array;->config_autoBrightnessLevels:I Lcom/android/internal/R$array;->config_mobile_hotspot_provision_app:I Lcom/android/internal/R$array;->config_sms_enabled_locking_shift_tables:I Lcom/android/internal/R$array;->config_sms_enabled_single_shift_tables:I @@ -7607,6 +7693,7 @@ Lcom/android/internal/R$drawable;->maps_google_logo:I Lcom/android/internal/R$drawable;->no_tile_256:I Lcom/android/internal/R$drawable;->reticle:I Lcom/android/internal/R$drawable;->stat_sys_download:I +Lcom/android/internal/R$fraction;->config_autoBrightnessAdjustmentMaxGamma:I Lcom/android/internal/R$id;->account_name:I Lcom/android/internal/R$id;->account_type:I Lcom/android/internal/R$id;->alertTitle:I @@ -7683,6 +7770,8 @@ Lcom/android/internal/R$id;->year:I Lcom/android/internal/R$id;->zoomControls:I Lcom/android/internal/R$id;->zoomMagnify:I Lcom/android/internal/R$integer;->config_screenBrightnessDim:I +Lcom/android/internal/R$integer;->config_screenBrightnessSettingMaximum:I +Lcom/android/internal/R$integer;->config_screenBrightnessSettingMinimum:I Lcom/android/internal/R$integer;->config_toastDefaultGravity:I Lcom/android/internal/R$interpolator;->accelerate_cubic:I Lcom/android/internal/R$interpolator;->decelerate_cubic:I @@ -7862,6 +7951,8 @@ Lcom/android/internal/R$styleable;->ListView_overScrollFooter:I Lcom/android/internal/R$styleable;->ListView_overScrollHeader:I Lcom/android/internal/R$styleable;->MapView:[I Lcom/android/internal/R$styleable;->MapView_apiKey:I +Lcom/android/internal/R$styleable;->MenuGroup:[I +Lcom/android/internal/R$styleable;->MenuItem:[I Lcom/android/internal/R$styleable;->MenuView:[I Lcom/android/internal/R$styleable;->PopupWindow:[I Lcom/android/internal/R$styleable;->PopupWindow_popupAnimationStyle:I @@ -8092,6 +8183,8 @@ Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->endCallForSubscriber(I)Z Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String; Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->mRemote:Landroid/os/IBinder; Lcom/android/internal/telephony/ITelephony$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephony; +Lcom/android/internal/telephony/ITelephony$Stub;->DESCRIPTOR:Ljava/lang/String; +Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_answerRingingCall:I Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_call:I Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_endCall:I Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_getDeviceId:I @@ -8250,6 +8343,7 @@ Lcom/android/internal/view/menu/ActionMenuItemView;->hasText()Z Lcom/android/internal/view/menu/MenuBuilder$Callback;->onMenuItemSelected(Lcom/android/internal/view/menu/MenuBuilder;Landroid/view/MenuItem;)Z Lcom/android/internal/view/menu/MenuBuilder$Callback;->onMenuModeChange(Lcom/android/internal/view/menu/MenuBuilder;)V Lcom/android/internal/view/menu/MenuBuilder;-><init>(Landroid/content/Context;)V +Lcom/android/internal/view/menu/MenuBuilder;->addMenuPresenter(Lcom/android/internal/view/menu/MenuPresenter;)V Lcom/android/internal/view/menu/MenuBuilder;->addMenuPresenter(Lcom/android/internal/view/menu/MenuPresenter;Landroid/content/Context;)V Lcom/android/internal/view/menu/MenuBuilder;->collapseItemActionView(Lcom/android/internal/view/menu/MenuItemImpl;)Z Lcom/android/internal/view/menu/MenuBuilder;->getContext()Landroid/content/Context; @@ -8257,6 +8351,7 @@ Lcom/android/internal/view/menu/MenuBuilder;->getNonActionItems()Ljava/util/Arra Lcom/android/internal/view/menu/MenuBuilder;->getRootMenu()Lcom/android/internal/view/menu/MenuBuilder; Lcom/android/internal/view/menu/MenuBuilder;->getVisibleItems()Ljava/util/ArrayList; Lcom/android/internal/view/menu/MenuBuilder;->mContext:Landroid/content/Context; +Lcom/android/internal/view/menu/MenuBuilder;->removeMenuPresenter(Lcom/android/internal/view/menu/MenuPresenter;)V Lcom/android/internal/view/menu/MenuBuilder;->setCallback(Lcom/android/internal/view/menu/MenuBuilder$Callback;)V Lcom/android/internal/view/menu/MenuBuilder;->setCurrentMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V Lcom/android/internal/view/menu/MenuBuilder;->setDefaultShowAsAction(I)Lcom/android/internal/view/menu/MenuBuilder; @@ -8435,6 +8530,7 @@ Ldalvik/system/DexFile;->openDexFileNative(Ljava/lang/String;Ljava/lang/String;I Ldalvik/system/DexPathList$Element;-><init>(Ldalvik/system/DexFile;Ljava/io/File;)V Ldalvik/system/DexPathList$Element;-><init>(Ljava/io/File;ZLjava/io/File;Ldalvik/system/DexFile;)V Ldalvik/system/DexPathList$Element;->dexFile:Ldalvik/system/DexFile; +Ldalvik/system/DexPathList$Element;->path:Ljava/io/File; Ldalvik/system/DexPathList$NativeLibraryElement;-><init>(Ljava/io/File;)V Ldalvik/system/DexPathList$NativeLibraryElement;->path:Ljava/io/File; Ldalvik/system/DexPathList;-><init>(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)V diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index a591eafea411..54cb09596479 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -4922,7 +4922,8 @@ public class Notification implements Parcelable CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY); if (!p.ambient && validRemoteInput && replyText != null - && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) { + && replyText.length > 0 && !TextUtils.isEmpty(replyText[0]) + && p.maxRemoteInputHistory > 0) { boolean showSpinner = mN.extras.getBoolean(EXTRA_SHOW_REMOTE_INPUT_SPINNER); big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE); big.setViewVisibility(R.id.notification_material_reply_text_1_container, @@ -4937,13 +4938,15 @@ public class Notification implements Parcelable ColorStateList.valueOf( isColorized() ? getPrimaryTextColor() : resolveContrastColor())); - if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) { + if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1]) + && p.maxRemoteInputHistory > 1) { big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE); big.setTextViewText(R.id.notification_material_reply_text_2, processTextSpans(replyText[1])); setTextViewColorSecondary(big, R.id.notification_material_reply_text_2); - if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) { + if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2]) + && p.maxRemoteInputHistory > 2) { big.setViewVisibility( R.id.notification_material_reply_text_3, View.VISIBLE); big.setTextViewText(R.id.notification_material_reply_text_3, @@ -5106,7 +5109,13 @@ public class Notification implements Parcelable return null; } - return applyStandardTemplateWithActions(getBigBaseLayoutResource(), null /* result */); + // We only want at most a single remote input history to be shown here, otherwise + // the content would become squished. + StandardTemplateParams p = mParams.reset().fillTextsFrom(this) + .setMaxRemoteInputHistory(1); + return applyStandardTemplateWithActions(getBigBaseLayoutResource(), + p, + null /* result */); } /** @@ -5975,6 +5984,12 @@ public class Notification implements Parcelable * object. */ public static abstract class Style { + + /** + * The number of items allowed simulatanously in the remote input history. + * @hide + */ + static final int MAX_REMOTE_INPUT_HISTORY_LINES = 3; private CharSequence mBigContentTitle; /** @@ -7376,7 +7391,14 @@ public class Notification implements Parcelable return messages; } - static Message getMessageFromBundle(Bundle bundle) { + /** + * @return The message that is stored in the bundle or null if the message couldn't be + * resolved. + * + * @hide + */ + @Nullable + public static Message getMessageFromBundle(Bundle bundle) { try { if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) { return null; @@ -7434,6 +7456,11 @@ public class Notification implements Parcelable * @see Notification#bigContentView */ public static class InboxStyle extends Style { + + /** + * The number of lines of remote input history allowed until we start reducing lines. + */ + private static final int NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION = 1; private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5); public InboxStyle() { @@ -7533,6 +7560,28 @@ public class Notification implements Parcelable if (mBuilder.mActions.size() > 0) { maxRows--; } + CharSequence[] remoteInputHistory = mBuilder.mN.extras.getCharSequenceArray( + EXTRA_REMOTE_INPUT_HISTORY); + if (remoteInputHistory != null + && remoteInputHistory.length > NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION) { + // Let's remove some messages to make room for the remote input history. + // 1 is always able to fit, but let's remove them if they are 2 or 3 + int numRemoteInputs = Math.min(remoteInputHistory.length, + MAX_REMOTE_INPUT_HISTORY_LINES); + int totalNumRows = mTexts.size() + numRemoteInputs + - NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION; + if (totalNumRows > maxRows) { + int overflow = totalNumRows - maxRows; + if (mTexts.size() > maxRows) { + // Heuristic: if the Texts don't fit anyway, we'll rather drop the last + // few messages, even with the remote input + maxRows -= overflow; + } else { + // otherwise we drop the first messages + i = overflow; + } + } + } while (i < mTexts.size() && i < maxRows) { CharSequence str = mTexts.get(i); if (!TextUtils.isEmpty(str)) { @@ -9603,6 +9652,7 @@ public class Notification implements Parcelable CharSequence title; CharSequence text; CharSequence headerTextSecondary; + int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES; boolean hideLargeIcon; boolean hideReplyIcon; @@ -9612,6 +9662,7 @@ public class Notification implements Parcelable title = null; text = null; headerTextSecondary = null; + maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES; return this; } @@ -9663,5 +9714,15 @@ public class Notification implements Parcelable this.text = b.processLegacyText(text, ambient); return this; } + + /** + * Set the maximum lines of remote input history lines allowed. + * @param maxRemoteInputHistory The number of lines. + * @return The builder for method chaining. + */ + public StandardTemplateParams setMaxRemoteInputHistory(int maxRemoteInputHistory) { + this.maxRemoteInputHistory = maxRemoteInputHistory; + return this; + } } } diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index fc5ea6607d87..30256b42e6a1 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -124,7 +124,7 @@ public class ResourcesManager { /** * The ApkAssets we are caching and intend to hold strong references to. */ - private final LruCache<ApkKey, ApkAssets> mLoadedApkAssets = new LruCache<>(15); + private final LruCache<ApkKey, ApkAssets> mLoadedApkAssets = new LruCache<>(3); /** * The ApkAssets that are being referenced in the wild that we can reuse, even if they aren't diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java index 45754aee39c3..32fc0dcebbc8 100644 --- a/core/java/android/app/StatsManager.java +++ b/core/java/android/app/StatsManager.java @@ -115,8 +115,9 @@ public final class StatsManager { } } + // TODO: Temporary for backwards compatibility. Remove. /** - * TODO: Temporary for backwards compatibility. Remove. + * @deprecated Use {@link #addConfig(long, byte[])} */ @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public boolean addConfiguration(long configKey, byte[] config) { @@ -149,8 +150,9 @@ public final class StatsManager { } } + // TODO: Temporary for backwards compatibility. Remove. /** - * TODO: Temporary for backwards compatibility. Remove. + * @deprecated Use {@link #removeConfig(long)} */ @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public boolean removeConfiguration(long configKey) { @@ -216,8 +218,9 @@ public final class StatsManager { } } + // TODO: Temporary for backwards compatibility. Remove. /** - * TODO: Temporary for backwards compatibility. Remove. + * @deprecated Use {@link #setBroadcastSubscriber(PendingIntent, long, long)} */ @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public boolean setBroadcastSubscriber( @@ -268,8 +271,9 @@ public final class StatsManager { } } + // TODO: Temporary for backwards compatibility. Remove. /** - * TODO: Temporary for backwards compatibility. Remove. + * @deprecated Use {@link #setFetchReportsOperation(PendingIntent, long)} */ @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) { @@ -304,8 +308,9 @@ public final class StatsManager { } } + // TODO: Temporary for backwards compatibility. Remove. /** - * TODO: Temporary for backwards compatibility. Remove. + * @deprecated Use {@link #getReports(long)} */ @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public @Nullable byte[] getData(long configKey) { @@ -339,12 +344,9 @@ public final class StatsManager { } } + // TODO: Temporary for backwards compatibility. Remove. /** - * Clients can request metadata for statsd. Will contain stats across all configurations but not - * the actual metrics themselves (metrics must be collected via {@link #getReports(long)}. - * This getter is not destructive and will not reset any metrics/counters. - * - * @return Serialized StatsdStatsReport proto. Returns null on failure (eg, if statsd crashed). + * @deprecated Use {@link #getStatsMetadata()} */ @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public @Nullable byte[] getMetadata() { diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java index f682ec1c31d4..ad862fcd86ff 100644 --- a/core/java/android/app/slice/SliceManager.java +++ b/core/java/android/app/slice/SliceManager.java @@ -245,7 +245,8 @@ public class SliceManager { ContentResolver resolver = mContext.getContentResolver(); try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) { if (provider == null) { - throw new IllegalArgumentException("Unknown URI " + uri); + Log.w(TAG, String.format("Unknown URI: %s", uri)); + return null; } Bundle extras = new Bundle(); extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri); @@ -306,7 +307,8 @@ public class SliceManager { .authority(authority).build(); try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) { if (provider == null) { - throw new IllegalArgumentException("Unknown URI " + uri); + Log.w(TAG, String.format("Unknown URI: %s", uri)); + return null; } Bundle extras = new Bundle(); extras.putParcelable(SliceProvider.EXTRA_INTENT, intent); @@ -383,7 +385,8 @@ public class SliceManager { .authority(authority).build(); try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) { if (provider == null) { - throw new IllegalArgumentException("Unknown URI " + uri); + Log.w(TAG, String.format("Unknown URI: %s", uri)); + return null; } Bundle extras = new Bundle(); extras.putParcelable(SliceProvider.EXTRA_INTENT, intent); diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java index 2306e5fa3dc1..16c1f6d59ca3 100644 --- a/core/java/android/inputmethodservice/KeyboardView.java +++ b/core/java/android/inputmethodservice/KeyboardView.java @@ -21,18 +21,15 @@ import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Paint.Align; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Typeface; -import android.graphics.Paint.Align; -import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.inputmethodservice.Keyboard.Key; import android.media.AudioManager; import android.os.Handler; import android.os.Message; -import android.os.UserHandle; -import android.provider.Settings; import android.util.AttributeSet; import android.util.TypedValue; import android.view.GestureDetector; @@ -662,11 +659,13 @@ public class KeyboardView extends View implements View.OnClickListener { invalidateAllKeys(); mKeyboardChanged = false; } - final Canvas canvas = mCanvas; - canvas.clipRect(mDirtyRect, Op.REPLACE); if (mKeyboard == null) return; + mCanvas.save(); + final Canvas canvas = mCanvas; + canvas.clipRect(mDirtyRect); + final Paint paint = mPaint; final Drawable keyBackground = mKeyBackground; final Rect clipRegion = mClipRegion; @@ -758,7 +757,7 @@ public class KeyboardView extends View implements View.OnClickListener { paint.setColor(0xFF00FF00); canvas.drawCircle((mStartX + mLastX) / 2, (mStartY + mLastY) / 2, 2, paint); } - + mCanvas.restore(); mDrawPending = false; mDirtyRect.setEmpty(); } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index c3b8f3959fec..c5cb1f5b7cf8 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -238,6 +238,14 @@ public class ConnectivityManager { public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL"; /** + * Key for passing a {@link android.net.captiveportal.CaptivePortalProbeSpec} to the captive + * portal login activity. + * {@hide} + */ + public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = + "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC"; + + /** * Key for passing a user agent string to the captive portal login activity. * {@hide} */ diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index 15a0ee5eb2a6..d75d4391dee3 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -85,6 +85,21 @@ public class Network implements Parcelable { private static final long HANDLE_MAGIC = 0xcafed00dL; private static final int HANDLE_MAGIC_SIZE = 32; + // A boolean to control how getAllByName()/getByName() behaves in the face + // of Private DNS. + // + // When true, these calls will request that DNS resolution bypass any + // Private DNS that might otherwise apply. Use of this feature is restricted + // and permission checks are made by netd (attempts to bypass Private DNS + // without appropriate permission are silently turned into vanilla DNS + // requests). This only affects DNS queries made using this network object. + // + // It it not parceled to receivers because (a) it can be set or cleared at + // anytime and (b) receivers should be explicit about attempts to bypass + // Private DNS so that the intent of the code is easily determined and + // code search audits are possible. + private boolean mPrivateDnsBypass = false; + /** * @hide */ @@ -108,7 +123,7 @@ public class Network implements Parcelable { * @throws UnknownHostException if the address lookup fails. */ public InetAddress[] getAllByName(String host) throws UnknownHostException { - return InetAddress.getAllByNameOnNet(host, netId); + return InetAddress.getAllByNameOnNet(host, getNetIdForResolv()); } /** @@ -122,7 +137,32 @@ public class Network implements Parcelable { * if the address lookup fails. */ public InetAddress getByName(String host) throws UnknownHostException { - return InetAddress.getByNameOnNet(host, netId); + return InetAddress.getByNameOnNet(host, getNetIdForResolv()); + } + + /** + * Specify whether or not Private DNS should be bypassed when attempting + * to use {@link getAllByName()}/{@link getByName()} methods on the given + * instance for hostname resolution. + * + * @hide + */ + public void setPrivateDnsBypass(boolean bypass) { + mPrivateDnsBypass = bypass; + } + + /** + * Returns a netid marked with the Private DNS bypass flag. + * + * This flag must be kept in sync with the NETID_USE_LOCAL_NAMESERVERS flag + * in system/netd/include/NetdClient.h. + * + * @hide + */ + public int getNetIdForResolv() { + return mPrivateDnsBypass + ? (int) (0x80000000L | (long) netId) // Non-portable DNS resolution flag. + : netId; } /** diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index a808c6426945..e3a110706c45 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -63,16 +63,7 @@ public final class NetworkCapabilities implements Parcelable { public NetworkCapabilities(NetworkCapabilities nc) { if (nc != null) { - mNetworkCapabilities = nc.mNetworkCapabilities; - mTransportTypes = nc.mTransportTypes; - mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps; - mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps; - mNetworkSpecifier = nc.mNetworkSpecifier; - mSignalStrength = nc.mSignalStrength; - mUids = nc.mUids; - mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid; - mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities; - mSSID = nc.mSSID; + set(nc); } } @@ -92,6 +83,23 @@ public final class NetworkCapabilities implements Parcelable { } /** + * Set all contents of this object to the contents of a NetworkCapabilities. + * @hide + */ + public void set(NetworkCapabilities nc) { + mNetworkCapabilities = nc.mNetworkCapabilities; + mTransportTypes = nc.mTransportTypes; + mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps; + mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps; + mNetworkSpecifier = nc.mNetworkSpecifier; + mSignalStrength = nc.mSignalStrength; + setUids(nc.mUids); // Will make the defensive copy + mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid; + mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities; + mSSID = nc.mSSID; + } + + /** * Represents the network's capabilities. If any are specified they will be satisfied * by any Network that matches all of them. */ diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 227a4cb2c516..16c2342a89b5 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -198,8 +198,7 @@ public class NetworkRequest implements Parcelable { * @hide */ public Builder setCapabilities(NetworkCapabilities nc) { - mNetworkCapabilities.clearAll(); - mNetworkCapabilities.combineCapabilities(nc); + mNetworkCapabilities.set(nc); return this; } diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index f0dd2629f93c..edf9bc1c6b39 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -1100,6 +1100,8 @@ public class NetworkStats implements Parcelable { public interface NonMonotonicObserver<C> { public void foundNonMonotonic( NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, C cookie); + public void foundNonMonotonic( + NetworkStats stats, int statsIndex, C cookie); } /** diff --git a/core/java/android/net/SntpClient.java b/core/java/android/net/SntpClient.java index 66cdc99150a5..ddf63caf06bf 100644 --- a/core/java/android/net/SntpClient.java +++ b/core/java/android/net/SntpClient.java @@ -84,6 +84,10 @@ public class SntpClient { * @return true if the transaction was successful. */ public boolean requestTime(String host, int timeout, Network network) { + // This flag only affects DNS resolution and not other socket semantics, + // therefore it's safe to set unilaterally rather than take more + // defensive measures like making a copy. + network.setPrivateDnsBypass(true); InetAddress address = null; try { address = network.getByName(host); diff --git a/core/java/android/net/captiveportal/CaptivePortalProbeResult.java b/core/java/android/net/captiveportal/CaptivePortalProbeResult.java new file mode 100644 index 000000000000..1634694cc71f --- /dev/null +++ b/core/java/android/net/captiveportal/CaptivePortalProbeResult.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2018 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.net.captiveportal; + +import android.annotation.Nullable; + +/** + * Result of calling isCaptivePortal(). + * @hide + */ +public final class CaptivePortalProbeResult { + public static final int SUCCESS_CODE = 204; + public static final int FAILED_CODE = 599; + public static final int PORTAL_CODE = 302; + + public static final CaptivePortalProbeResult FAILED = new CaptivePortalProbeResult(FAILED_CODE); + public static final CaptivePortalProbeResult SUCCESS = + new CaptivePortalProbeResult(SUCCESS_CODE); + + private final int mHttpResponseCode; // HTTP response code returned from Internet probe. + public final String redirectUrl; // Redirect destination returned from Internet probe. + public final String detectUrl; // URL where a 204 response code indicates + // captive portal has been appeased. + @Nullable + public final CaptivePortalProbeSpec probeSpec; + + public CaptivePortalProbeResult(int httpResponseCode) { + this(httpResponseCode, null, null); + } + + public CaptivePortalProbeResult(int httpResponseCode, String redirectUrl, String detectUrl) { + this(httpResponseCode, redirectUrl, detectUrl, null); + } + + public CaptivePortalProbeResult(int httpResponseCode, String redirectUrl, String detectUrl, + CaptivePortalProbeSpec probeSpec) { + mHttpResponseCode = httpResponseCode; + this.redirectUrl = redirectUrl; + this.detectUrl = detectUrl; + this.probeSpec = probeSpec; + } + + public boolean isSuccessful() { + return mHttpResponseCode == SUCCESS_CODE; + } + + public boolean isPortal() { + return !isSuccessful() && (mHttpResponseCode >= 200) && (mHttpResponseCode <= 399); + } + + public boolean isFailed() { + return !isSuccessful() && !isPortal(); + } +} diff --git a/core/java/android/net/captiveportal/CaptivePortalProbeSpec.java b/core/java/android/net/captiveportal/CaptivePortalProbeSpec.java new file mode 100644 index 000000000000..57a926afec54 --- /dev/null +++ b/core/java/android/net/captiveportal/CaptivePortalProbeSpec.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2018 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.net.captiveportal; + +import static android.net.captiveportal.CaptivePortalProbeResult.PORTAL_CODE; +import static android.net.captiveportal.CaptivePortalProbeResult.SUCCESS_CODE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.text.TextUtils; +import android.util.Log; + +import java.net.MalformedURLException; +import java.net.URL; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** @hide */ +public abstract class CaptivePortalProbeSpec { + public static final String HTTP_LOCATION_HEADER_NAME = "Location"; + + private static final String TAG = CaptivePortalProbeSpec.class.getSimpleName(); + private static final String REGEX_SEPARATOR = "@@/@@"; + private static final String SPEC_SEPARATOR = "@@,@@"; + + private final String mEncodedSpec; + private final URL mUrl; + + CaptivePortalProbeSpec(String encodedSpec, URL url) { + mEncodedSpec = encodedSpec; + mUrl = url; + } + + /** + * Parse a {@link CaptivePortalProbeSpec} from a {@link String}. + * + * <p>The valid format is a URL followed by two regular expressions, each separated by "@@/@@". + * @throws MalformedURLException The URL has invalid format for {@link URL#URL(String)}. + * @throws ParseException The string is empty, does not match the above format, or a regular + * expression is invalid for {@link Pattern#compile(String)}. + */ + @NonNull + public static CaptivePortalProbeSpec parseSpec(String spec) throws ParseException, + MalformedURLException { + if (TextUtils.isEmpty(spec)) { + throw new ParseException("Empty probe spec", 0 /* errorOffset */); + } + + String[] splits = TextUtils.split(spec, REGEX_SEPARATOR); + if (splits.length != 3) { + throw new ParseException("Probe spec does not have 3 parts", 0 /* errorOffset */); + } + + final int statusRegexPos = splits[0].length() + REGEX_SEPARATOR.length(); + final int locationRegexPos = statusRegexPos + splits[1].length() + REGEX_SEPARATOR.length(); + final Pattern statusRegex = parsePatternIfNonEmpty(splits[1], statusRegexPos); + final Pattern locationRegex = parsePatternIfNonEmpty(splits[2], locationRegexPos); + + return new RegexMatchProbeSpec(spec, new URL(splits[0]), statusRegex, locationRegex); + } + + @Nullable + private static Pattern parsePatternIfNonEmpty(String pattern, int pos) throws ParseException { + if (TextUtils.isEmpty(pattern)) { + return null; + } + try { + return Pattern.compile(pattern); + } catch (PatternSyntaxException e) { + throw new ParseException( + String.format("Invalid status pattern [%s]: %s", pattern, e), + pos /* errorOffset */); + } + } + + /** + * Parse a {@link CaptivePortalProbeSpec} from a {@link String}, or return a fallback spec + * based on the status code of the provided URL if the spec cannot be parsed. + */ + @Nullable + public static CaptivePortalProbeSpec parseSpecOrNull(@Nullable String spec) { + if (spec != null) { + try { + return parseSpec(spec); + } catch (ParseException | MalformedURLException e) { + Log.e(TAG, "Invalid probe spec: " + spec, e); + // Fall through + } + } + return null; + } + + /** + * Parse a config String to build an array of {@link CaptivePortalProbeSpec}. + * + * <p>Each spec is separated by @@,@@ and follows the format for {@link #parseSpec(String)}. + * <p>This method does not throw but ignores any entry that could not be parsed. + */ + public static CaptivePortalProbeSpec[] parseCaptivePortalProbeSpecs(String settingsVal) { + List<CaptivePortalProbeSpec> specs = new ArrayList<>(); + if (settingsVal != null) { + for (String spec : TextUtils.split(settingsVal, SPEC_SEPARATOR)) { + try { + specs.add(parseSpec(spec)); + } catch (ParseException | MalformedURLException e) { + Log.e(TAG, "Invalid probe spec: " + spec, e); + } + } + } + + if (specs.isEmpty()) { + Log.e(TAG, String.format("could not create any validation spec from %s", settingsVal)); + } + return specs.toArray(new CaptivePortalProbeSpec[specs.size()]); + } + + /** + * Get the probe result from HTTP status and location header. + */ + public abstract CaptivePortalProbeResult getResult(int status, @Nullable String locationHeader); + + public String getEncodedSpec() { + return mEncodedSpec; + } + + public URL getUrl() { + return mUrl; + } + + /** + * Implementation of {@link CaptivePortalProbeSpec} that is based on configurable regular + * expressions for the HTTP status code and location header (if any). Matches indicate that + * the page is not a portal. + * This probe cannot fail: it always returns SUCCESS_CODE or PORTAL_CODE + */ + private static class RegexMatchProbeSpec extends CaptivePortalProbeSpec { + @Nullable + final Pattern mStatusRegex; + @Nullable + final Pattern mLocationHeaderRegex; + + RegexMatchProbeSpec( + String spec, URL url, Pattern statusRegex, Pattern locationHeaderRegex) { + super(spec, url); + mStatusRegex = statusRegex; + mLocationHeaderRegex = locationHeaderRegex; + } + + @Override + public CaptivePortalProbeResult getResult(int status, String locationHeader) { + final boolean statusMatch = safeMatch(String.valueOf(status), mStatusRegex); + final boolean locationMatch = safeMatch(locationHeader, mLocationHeaderRegex); + final int returnCode = statusMatch && locationMatch ? SUCCESS_CODE : PORTAL_CODE; + return new CaptivePortalProbeResult( + returnCode, locationHeader, getUrl().toString(), this); + } + } + + private static boolean safeMatch(@Nullable String value, @Nullable Pattern pattern) { + // No value is a match ("no location header" passes the location rule for non-redirects) + return pattern == null || TextUtils.isEmpty(value) || pattern.matcher(value).matches(); + } +} diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 619ec235913e..221abed92f81 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -245,8 +245,10 @@ public abstract class BatteryStats implements Parcelable { * New in version 31: * - New cellular network types. * - Deferred job metrics. + * New in version 32: + * - Ambient display properly output in data dump. */ - static final int CHECKIN_VERSION = 31; + static final int CHECKIN_VERSION = 32; /** * Old version, we hit 9 and ran out of room, need to remove. diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 5e23932c48cc..3017f258ed2d 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -45,6 +45,7 @@ interface IUserManager { in String[] disallowedPackages); UserInfo createRestrictedProfile(String name, int parentUserHandle); void setUserEnabled(int userHandle); + void setUserAdmin(int userId); void evictCredentialEncryptionKey(int userHandle); boolean removeUser(int userHandle); boolean removeUserEvenWhenDisallowed(int userHandle); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 11afd21d60fc..b5918519d872 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -2087,12 +2087,33 @@ public class UserManager { * Also ephemeral users can be disabled to indicate that their removal is in progress and they * shouldn't be re-entered. Therefore ephemeral users should not be re-enabled once disabled. * - * @param userHandle the id of the profile to enable + * @param userId the id of the profile to enable * @hide */ - public void setUserEnabled(@UserIdInt int userHandle) { + public void setUserEnabled(@UserIdInt int userId) { try { - mService.setUserEnabled(userHandle); + mService.setUserEnabled(userId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Assigns admin privileges to the user, if such a user exists. + * + * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} and + * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permissions. + * + * @param userHandle the id of the user to become admin + * @hide + */ + @RequiresPermission(allOf = { + Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Manifest.permission.MANAGE_USERS + }) + public void setUserAdmin(@UserIdInt int userHandle) { + try { + mService.setUserAdmin(userHandle); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index 4d238c0c7723..ff5502f50655 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -17,6 +17,7 @@ package android.os; import android.annotation.Nullable; +import android.content.ContentResolver; import android.content.Context; import android.hardware.vibrator.V1_0.EffectStrength; import android.hardware.vibrator.V1_2.Effect; @@ -275,7 +276,12 @@ public abstract class VibrationEffect implements Parcelable { if (uris[i] == null) { continue; } - if (Uri.parse(uris[i]).equals(uri)) { + ContentResolver cr = context.getContentResolver(); + Uri mappedUri = cr.uncanonicalize(Uri.parse(uris[i])); + if (mappedUri == null) { + continue; + } + if (mappedUri.equals(uri)) { return get(RINGTONES[i]); } } diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java index 576ea5263280..8d4c3c3d3e68 100644 --- a/core/java/android/os/storage/VolumeInfo.java +++ b/core/java/android/os/storage/VolumeInfo.java @@ -407,8 +407,12 @@ public class VolumeInfo implements Parcelable { * {@link #TYPE_EMULATED} or {@link #TYPE_PUBLIC}. */ public @Nullable Intent buildBrowseIntent() { + return buildBrowseIntentForUser(UserHandle.myUserId()); + } + + public @Nullable Intent buildBrowseIntentForUser(int userId) { final Uri uri; - if (type == VolumeInfo.TYPE_PUBLIC && mountUserId == UserHandle.myUserId()) { + if (type == VolumeInfo.TYPE_PUBLIC && mountUserId == userId) { uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, fsUuid); } else if (type == VolumeInfo.TYPE_EMULATED && isPrimary()) { uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 7bf3af1f7738..05104182f2ec 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -23,6 +23,7 @@ import static android.provider.SettingsValidators.COMPONENT_NAME_VALIDATOR; import static android.provider.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR; import static android.provider.SettingsValidators.LOCALE_VALIDATOR; import static android.provider.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR; +import static android.provider.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR; import static android.provider.SettingsValidators.PACKAGE_NAME_VALIDATOR; import static android.provider.SettingsValidators.URI_VALIDATOR; @@ -3114,10 +3115,10 @@ public final class Settings { private static final Validator FONT_SCALE_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { try { return Float.parseFloat(value) >= 0; - } catch (NumberFormatException e) { + } catch (NumberFormatException | NullPointerException e) { return false; } } @@ -3655,11 +3656,11 @@ public final class Settings { /** @hide */ public static final Validator DATE_FORMAT_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { try { new SimpleDateFormat(value); return true; - } catch (IllegalArgumentException e) { + } catch (IllegalArgumentException | NullPointerException e) { return false; } } @@ -4066,7 +4067,7 @@ public final class Settings { /** @hide */ public static final Validator EGG_MODE_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { try { return Long.parseLong(value) >= 0; } catch (NumberFormatException e) { @@ -5452,7 +5453,8 @@ public final class Settings { @TestApi public static final String AUTOFILL_SERVICE = "autofill_service"; - private static final Validator AUTOFILL_SERVICE_VALIDATOR = COMPONENT_NAME_VALIDATOR; + private static final Validator AUTOFILL_SERVICE_VALIDATOR = + NULLABLE_COMPONENT_NAME_VALIDATOR; /** * Boolean indicating if Autofill supports field classification. @@ -5950,7 +5952,7 @@ public final class Settings { "accessibility_shortcut_target_service"; private static final Validator ACCESSIBILITY_SHORTCUT_TARGET_SERVICE_VALIDATOR = - COMPONENT_NAME_VALIDATOR; + NULLABLE_COMPONENT_NAME_VALIDATOR; /** * Setting specifying the accessibility service or feature to be toggled via the @@ -5965,7 +5967,7 @@ public final class Settings { private static final Validator ACCESSIBILITY_BUTTON_TARGET_COMPONENT_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { // technically either ComponentName or class name, but there's proper value // validation at callsites, so allow any non-null string return value != null; @@ -6482,7 +6484,7 @@ public final class Settings { private static final Validator TTS_DEFAULT_LOCALE_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { if (value == null || value.length() == 0) { return false; } @@ -7112,6 +7114,35 @@ public final class Settings { public static final String UI_NIGHT_MODE = "ui_night_mode"; /** + * The current device UI theme mode effect SystemUI and Launcher.<br/> + * <b>Values:</b><br/> + * 0 - The mode that theme will controlled by wallpaper color.<br/> + * 1 - The mode that will always light theme.<br/> + * 2 - The mode that will always dark theme.<br/> + * + * @hide + */ + public static final String THEME_MODE = "theme_mode"; + + /** + * THEME_MODE value for wallpaper mode. + * @hide + */ + public static final int THEME_MODE_WALLPAPER = 0; + + /** + * THEME_MODE value for light theme mode. + * @hide + */ + public static final int THEME_MODE_LIGHT = 1; + + /** + * THEME_MODE value for dark theme mode. + * @hide + */ + public static final int THEME_MODE_DARK = 2; + + /** * Whether screensavers are enabled. * @hide */ @@ -7720,7 +7751,7 @@ public final class Settings { private static final Validator QS_TILES_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { if (value == null) { return false; } @@ -7779,7 +7810,7 @@ public final class Settings { private static final Validator QS_AUTO_ADDED_TILES_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { if (value == null) { return false; } @@ -8653,7 +8684,7 @@ public final class Settings { private static final Validator STAY_ON_WHILE_PLUGGED_IN_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { try { int val = Integer.parseInt(value); return (val == 0) @@ -9687,7 +9718,7 @@ public final class Settings { private static final Validator USE_OPEN_WIFI_PACKAGE_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { return (value == null) || PACKAGE_NAME_VALIDATOR.validate(value); } }; @@ -10251,6 +10282,15 @@ public final class Settings { "captive_portal_other_fallback_urls"; /** + * A list of captive portal detection specifications used in addition to the fallback URLs. + * Each spec has the format url@@/@@statusCodeRegex@@/@@contentRegex. Specs are separated + * by "@@,@@". + * @hide + */ + public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS = + "captive_portal_fallback_probe_specs"; + + /** * Whether to use HTTPS for network validation. This is enabled by default and the setting * needs to be set to 0 to disable it. This setting is a misnomer because captive portals * don't actually use HTTPS, but it's consistent with the other settings. @@ -11382,7 +11422,7 @@ public final class Settings { private static final Validator ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { try { String[] surroundFormats = TextUtils.split(value, ","); for (String format : surroundFormats) { @@ -11564,6 +11604,13 @@ public final class Settings { /** @hide */ public static final int EMULATE_DISPLAY_CUTOUT_ON = 1; /** + * A colon separated list of keys for Settings Slices. + * + * @hide + */ + public static final String BLOCKED_SLICES = "blocked_slices"; + + /** * Defines global zen mode. ZEN_MODE_OFF, ZEN_MODE_IMPORTANT_INTERRUPTIONS, * or ZEN_MODE_NO_INTERRUPTIONS. * diff --git a/core/java/android/provider/SettingsValidators.java b/core/java/android/provider/SettingsValidators.java index c878bba4edfe..25e77867b757 100644 --- a/core/java/android/provider/SettingsValidators.java +++ b/core/java/android/provider/SettingsValidators.java @@ -16,6 +16,7 @@ package android.provider; +import android.annotation.Nullable; import android.content.ComponentName; import android.net.Uri; @@ -36,14 +37,14 @@ public class SettingsValidators { public static final Validator ANY_STRING_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { return true; } }; public static final Validator NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { try { return Integer.parseInt(value) >= 0; } catch (NumberFormatException e) { @@ -54,7 +55,7 @@ public class SettingsValidators { public static final Validator ANY_INTEGER_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { try { Integer.parseInt(value); return true; @@ -66,7 +67,7 @@ public class SettingsValidators { public static final Validator URI_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { try { Uri.decode(value); return true; @@ -76,16 +77,31 @@ public class SettingsValidators { } }; + /** + * Does not allow a setting to have a null {@link ComponentName}. Use {@link + * SettingsValidators#NULLABLE_COMPONENT_NAME_VALIDATOR} instead if a setting can have a + * nullable {@link ComponentName}. + */ public static final Validator COMPONENT_NAME_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { return value != null && ComponentName.unflattenFromString(value) != null; } }; + /** + * Allows a setting to have a null {@link ComponentName}. + */ + public static final Validator NULLABLE_COMPONENT_NAME_VALIDATOR = new Validator() { + @Override + public boolean validate(@Nullable String value) { + return value == null || COMPONENT_NAME_VALIDATOR.validate(value); + } + }; + public static final Validator PACKAGE_NAME_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { return value != null && isStringPackageName(value); } @@ -122,7 +138,7 @@ public class SettingsValidators { private static final int MAX_IPV6_LENGTH = 45; @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { if (value == null) { return false; } @@ -132,7 +148,7 @@ public class SettingsValidators { public static final Validator LOCALE_VALIDATOR = new Validator() { @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { if (value == null) { return false; } @@ -147,7 +163,11 @@ public class SettingsValidators { }; public interface Validator { - boolean validate(String value); + /** + * Returns whether the input value is valid. Subclasses should handle the case where the + * input value is {@code null}. + */ + boolean validate(@Nullable String value); } public static final class DiscreteValueValidator implements Validator { @@ -158,7 +178,7 @@ public class SettingsValidators { } @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { return ArrayUtils.contains(mValues, value); } } @@ -173,7 +193,7 @@ public class SettingsValidators { } @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { try { final int intValue = Integer.parseInt(value); return intValue >= mMin && intValue <= mMax; @@ -193,11 +213,11 @@ public class SettingsValidators { } @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { try { final float floatValue = Float.parseFloat(value); return floatValue >= mMin && floatValue <= mMax; - } catch (NumberFormatException e) { + } catch (NumberFormatException | NullPointerException e) { return false; } } @@ -211,7 +231,7 @@ public class SettingsValidators { } @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { if (value == null) { return false; } @@ -233,7 +253,7 @@ public class SettingsValidators { } @Override - public boolean validate(String value) { + public boolean validate(@Nullable String value) { if (value == null) { return false; } diff --git a/core/java/android/webkit/SslErrorHandler.java b/core/java/android/webkit/SslErrorHandler.java index 537065d58a9d..1d5c6f1f12b1 100644 --- a/core/java/android/webkit/SslErrorHandler.java +++ b/core/java/android/webkit/SslErrorHandler.java @@ -36,6 +36,10 @@ public class SslErrorHandler extends Handler { /** * Proceed with the SSL certificate. + * <p> + * It is not recommended to proceed past SSL errors and this method should + * generally not be used; see {@link WebViewClient#onReceivedSslError} for + * more information. */ public void proceed() {} diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index 4aa1c4a6daff..95fe963d7891 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -113,6 +113,20 @@ public class WebChromeClient { * the new WebView as an argument. If the host application chooses not to * honor the request, it should return {@code false} from this method. The default * implementation of this method does nothing and hence returns {@code false}. + * <p> + * Applications should typically not allow windows to be created when the + * {@code isUserGesture} flag is false, as this may be an unwanted popup. + * <p> + * Applications should be careful how they display the new window: don't simply + * overlay it over the existing WebView as this may mislead the user about which + * site they are viewing. If your application displays the URL of the main page, + * make sure to also display the URL of the new window in a similar fashion. If + * your application does not display URLs, consider disallowing the creation of + * new windows entirely. + * <p class="note"><b>Note:</b> There is no trustworthy way to tell which page + * requested the new window: the request might originate from a third-party iframe + * inside the WebView. + * * @param view The WebView from which the request for a new window * originated. * @param isDialog {@code true} if the new window should be a dialog, rather than @@ -149,6 +163,11 @@ public class WebChromeClient { * from the view system if necessary. At this point, WebCore has stopped * any loading in this window and has removed any cross-scripting ability * in javascript. + * <p> + * As with {@link #onCreateWindow}, the application should ensure that any + * URL or security indicator displayed is updated so that the user can tell + * that the page they were interacting with has been closed. + * * @param window The WebView that needs to be closed. */ public void onCloseWindow(WebView window) {} diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 10748acd4c51..d7f1d6e3b2d1 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -1019,11 +1019,21 @@ public class WebView extends AbsoluteLayout * If {@code null}, defaults to 'text/html'. * <p> * The 'data' scheme URL formed by this method uses the default US-ASCII - * charset. If you need need to set a different charset, you should form a + * charset. If you need to set a different charset, you should form a * 'data' scheme URL which explicitly specifies a charset parameter in the * mediatype portion of the URL and call {@link #loadUrl(String)} instead. * Note that the charset obtained from the mediatype portion of a data URL * always overrides that specified in the HTML or XML document itself. + * <p> + * Content loaded using this method will have a {@code window.origin} value + * of {@code "null"}. This must not be considered to be a trusted origin + * by the application or by any JavaScript code running inside the WebView + * (for example, event sources in DOM event handlers or web messages), + * because malicious content can also create frames with a null origin. If + * you need to identify the main frame's origin in a trustworthy way, you + * should use {@link #loadDataWithBaseURL(String,String,String,String,String) + * loadDataWithBaseURL()} with a valid HTTP or HTTPS base URL to set the + * origin. * * @param data a String of data in the given encoding * @param mimeType the MIME type of the data, e.g. 'text/html'. @@ -1057,6 +1067,15 @@ public class WebView extends AbsoluteLayout * <p> * Note that the baseUrl is sent in the 'Referer' HTTP header when * requesting subresources (images, etc.) of the page loaded using this method. + * <p> + * If a valid HTTP or HTTPS base URL is not specified in {@code baseUrl}, then + * content loaded using this method will have a {@code window.origin} value + * of {@code "null"}. This must not be considered to be a trusted origin + * by the application or by any JavaScript code running inside the WebView + * (for example, event sources in DOM event handlers or web messages), + * because malicious content can also create frames with a null origin. If + * you need to identify the main frame's origin in a trustworthy way, you + * should use a valid HTTP or HTTPS base URL to set the origin. * * @param baseUrl the URL to use as the page's base URL. If {@code null} defaults to * 'about:blank'. @@ -2056,6 +2075,13 @@ public class WebView extends AbsoluteLayout * <p> * A target origin can be set as a wildcard ("*"). However this is not recommended. * See the page above for security issues. + * <p> + * Content loaded via {@link #loadData(String,String,String)} will not have a + * valid origin, and thus cannot be sent messages securely. If you need to send + * messages using this function, you should use + * {@link #loadDataWithBaseURL(String,String,String,String,String)} with a valid + * HTTP or HTTPS {@code baseUrl} to define a valid origin that can be used for + * messaging. * * @param message the WebMessage * @param targetOrigin the target origin. diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index 6df165551f9f..f686b6645655 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -368,6 +368,15 @@ public class WebViewClient { * handler.proceed(). Note that the decision may be retained for use in * response to future SSL errors. The default behavior is to cancel the * load. + * <p> + * Applications are advised not to prompt the user about SSL errors, as + * the user is unlikely to be able to make an informed security decision + * and WebView does not provide any UI for showing the details of the + * error in a meaningful way. + * <p> + * Application overrides of this method may display custom error pages or + * silently log issues, but it is strongly recommended to always call + * handler.cancel() and never allow proceeding past errors. * * @param view The WebView that is initiating the callback. * @param handler An SslErrorHandler object that will handle the user's diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index 8f1f1ab2e5c0..d22b1e66b596 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -284,7 +284,12 @@ public final class SelectionActionModeHelper { @Nullable SelectionResult result) { final Layout layout = mTextView.getLayout(); - final Runnable onAnimationEndCallback = () -> startSelectionActionMode(result); + final Runnable onAnimationEndCallback = () -> { + if (result.mStart >= 0 && result.mEnd <= getText(mTextView).length() + && result.mStart <= result.mEnd) { + startSelectionActionMode(result); + } + }; // TODO do not trigger the animation if the change included only non-printable characters final boolean didSelectionChange = result != null && (mTextView.getSelectionStart() != result.mStart @@ -636,7 +641,7 @@ public final class SelectionActionModeHelper { mSelectionStart, mSelectionEnd, SelectionEvent.ACTION_ABANDON, null /* classification */); mSelectionStart = mSelectionEnd = -1; - mTextView.getTextClassificationSession().destroy(); + mLogger.endTextClassificationSession(); mIsPending = false; } } @@ -702,8 +707,10 @@ public final class SelectionActionModeHelper { mTokenIterator.setText(mText); mStartIndex = index; mClassificationSession = classificationSession; - mClassificationSession.onSelectionEvent( - SelectionEvent.createSelectionStartedEvent(invocationMethod, 0)); + if (hasActiveClassificationSession()) { + mClassificationSession.onSelectionEvent( + SelectionEvent.createSelectionStartedEvent(invocationMethod, 0)); + } } catch (Exception e) { // Avoid crashes due to logging. Log.e(LOG_TAG, "" + e.getMessage(), e); @@ -713,23 +720,19 @@ public final class SelectionActionModeHelper { public void logSelectionModified(int start, int end, @Nullable TextClassification classification, @Nullable TextSelection selection) { try { - Preconditions.checkArgumentInRange(start, 0, mText.length(), "start"); - Preconditions.checkArgumentInRange(end, start, mText.length(), "end"); - int[] wordIndices = getWordDelta(start, end); - if (selection != null) { - if (mClassificationSession != null) { + if (hasActiveClassificationSession()) { + Preconditions.checkArgumentInRange(start, 0, mText.length(), "start"); + Preconditions.checkArgumentInRange(end, start, mText.length(), "end"); + int[] wordIndices = getWordDelta(start, end); + if (selection != null) { mClassificationSession.onSelectionEvent( SelectionEvent.createSelectionModifiedEvent( wordIndices[0], wordIndices[1], selection)); - } - } else if (classification != null) { - if (mClassificationSession != null) { + } else if (classification != null) { mClassificationSession.onSelectionEvent( SelectionEvent.createSelectionModifiedEvent( wordIndices[0], wordIndices[1], classification)); - } - } else { - if (mClassificationSession != null) { + } else { mClassificationSession.onSelectionEvent( SelectionEvent.createSelectionModifiedEvent( wordIndices[0], wordIndices[1])); @@ -746,21 +749,23 @@ public final class SelectionActionModeHelper { @SelectionEvent.ActionType int action, @Nullable TextClassification classification) { try { - Preconditions.checkArgumentInRange(start, 0, mText.length(), "start"); - Preconditions.checkArgumentInRange(end, start, mText.length(), "end"); - int[] wordIndices = getWordDelta(start, end); - if (classification != null) { - if (mClassificationSession != null) { + if (hasActiveClassificationSession()) { + Preconditions.checkArgumentInRange(start, 0, mText.length(), "start"); + Preconditions.checkArgumentInRange(end, start, mText.length(), "end"); + int[] wordIndices = getWordDelta(start, end); + if (classification != null) { mClassificationSession.onSelectionEvent( SelectionEvent.createSelectionActionEvent( - wordIndices[0], wordIndices[1], action, classification)); - } - } else { - if (mClassificationSession != null) { + wordIndices[0], wordIndices[1], action, + classification)); + } else { mClassificationSession.onSelectionEvent( SelectionEvent.createSelectionActionEvent( wordIndices[0], wordIndices[1], action)); } + if (SelectionEvent.isTerminal(action)) { + endTextClassificationSession(); + } } } catch (Exception e) { // Avoid crashes due to logging. @@ -772,6 +777,16 @@ public final class SelectionActionModeHelper { return mEditTextLogger; } + public void endTextClassificationSession() { + if (hasActiveClassificationSession()) { + mClassificationSession.destroy(); + } + } + + private boolean hasActiveClassificationSession() { + return mClassificationSession != null && !mClassificationSession.isDestroyed(); + } + private int[] getWordDelta(int start, int end) { int[] wordIndices = new int[2]; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 48775adc83cf..a57d154532eb 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -11527,12 +11527,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } else { widgetType = TextClassifier.WIDGET_TYPE_UNSELECTABLE_TEXTVIEW; } - // TODO: Tagged this widgetType with a * so it we can monitor if it reports - // SelectionEvents exactly as the older Logger does. Remove once investigations - // are complete. final TextClassificationContext textClassificationContext = new TextClassificationContext.Builder( - mContext.getPackageName(), "*" + widgetType) + mContext.getPackageName(), widgetType) .build(); if (mTextClassifier != null) { mTextClassificationSession = tcm.createTextClassificationSession( diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 97043c72dc5d..571878d183ee 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.annotation.NonNull; import android.app.Activity; +import android.app.ActivityManager; import android.app.usage.UsageStatsManager; import android.content.ComponentName; import android.content.Context; @@ -1022,6 +1023,11 @@ public class ChooserActivity extends ResolverActivity { @Override public void onListRebuilt() { + // don't support direct share on low ram devices + if (ActivityManager.isLowRamDeviceStatic()) { + return; + } + if (mServiceTargets != null) { pruneServiceTargets(); } diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java index 1811800c7b69..26fb6b6415e4 100644 --- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java +++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java @@ -59,8 +59,11 @@ public class AmbientDisplayConfiguration { } public boolean pulseOnPickupAvailable() { - return mContext.getResources().getBoolean(R.bool.config_dozePulsePickup) - && ambientDisplayAvailable(); + return dozePulsePickupSensorAvailable() && ambientDisplayAvailable(); + } + + public boolean dozePulsePickupSensorAvailable() { + return mContext.getResources().getBoolean(R.bool.config_dozePulsePickup); } public boolean pulseOnPickupCanBeModified(int user) { @@ -73,7 +76,11 @@ public class AmbientDisplayConfiguration { } public boolean pulseOnDoubleTapAvailable() { - return !TextUtils.isEmpty(doubleTapSensorType()) && ambientDisplayAvailable(); + return doubleTapSensorAvailable() && ambientDisplayAvailable(); + } + + public boolean doubleTapSensorAvailable() { + return !TextUtils.isEmpty(doubleTapSensorType()); } public String doubleTapSensorType() { @@ -115,7 +122,7 @@ public class AmbientDisplayConfiguration { return boolSettingDefaultOff(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, user); } - private boolean ambientDisplayAvailable() { + public boolean ambientDisplayAvailable() { return !TextUtils.isEmpty(ambientDisplayComponent()); } diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java index 7116f3a92576..d1350403b32c 100644 --- a/core/java/com/android/internal/widget/MessagingGroup.java +++ b/core/java/com/android/internal/widget/MessagingGroup.java @@ -207,6 +207,8 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou mAvatarView.setTranslationY(0.0f); mSenderName.setAlpha(1.0f); mSenderName.setTranslationY(0.0f); + mIsolatedMessage = null; + mMessages = null; sInstancePool.release(MessagingGroup.this); } }; @@ -267,6 +269,11 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou @Override public int getMeasuredType() { + if (mIsolatedMessage != null) { + // We only want to show one group if we have an inline image, so let's return shortened + // to avoid displaying the other ones. + return MEASURED_SHORTENED; + } boolean hasNormal = false; for (int i = mMessageContainer.getChildCount() - 1; i >= 0; i--) { View child = mMessageContainer.getChildAt(i); @@ -292,9 +299,6 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } } } - if (mMessageContainer.getChildCount() == 0 && mIsolatedMessage != null) { - return mIsolatedMessage.getMeasuredType(); - } return MEASURED_NORMAL; } diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java index 9048f87ef512..c7ea781b2793 100644 --- a/core/java/com/android/internal/widget/NotificationActionListLayout.java +++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java @@ -206,7 +206,17 @@ public class NotificationActionListLayout extends LinearLayout { final boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0; int childTop; - int childLeft = centerAligned ? left + (right - left) / 2 - mTotalWidth / 2 : 0; + int childLeft; + if (centerAligned) { + childLeft = mPaddingLeft + left + (right - left) / 2 - mTotalWidth / 2; + } else { + childLeft = mPaddingLeft; + int absoluteGravity = Gravity.getAbsoluteGravity(Gravity.START, getLayoutDirection()); + if (absoluteGravity == Gravity.RIGHT) { + childLeft += right - left - mTotalWidth; + } + } + // Where bottom of child should go final int height = bottom - top; @@ -216,18 +226,6 @@ public class NotificationActionListLayout extends LinearLayout { final int count = getChildCount(); - final int layoutDirection = getLayoutDirection(); - switch (Gravity.getAbsoluteGravity(Gravity.START, layoutDirection)) { - case Gravity.RIGHT: - childLeft += mPaddingLeft + right - left - mTotalWidth; - break; - - case Gravity.LEFT: - default: - childLeft += mPaddingLeft; - break; - } - int start = 0; int dir = 1; //In case of RTL, start drawing from the last child. diff --git a/core/res/res/drawable/ic_camera.xml b/core/res/res/drawable/ic_camera.xml index 2921a689ef8a..707453154f61 100644 --- a/core/res/res/drawable/ic_camera.xml +++ b/core/res/res/drawable/ic_camera.xml @@ -19,9 +19,6 @@ Copyright (C) 2018 The Android Open Source Project android:viewportWidth="24.0" android:viewportHeight="24.0"> <path - android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0" - android:fillColor="#FFFFFF"/> - <path - android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z" - android:fillColor="#FFFFFF"/> + android:fillColor="#FFFFFFFF" + android:pathData="M20,5h-3.17L15,3H9L7.17,5H4C2.9,5 2,5.9 2,7v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V7C22,5.9 21.1,5 20,5zM20,19H4V7h16V19zM12,9c-2.21,0 -4,1.79 -4,4c0,2.21 1.79,4 4,4s4,-1.79 4,-4C16,10.79 14.21,9 12,9z"/> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_mic.xml b/core/res/res/drawable/ic_mic.xml index 3212330278aa..29389faf760e 100644 --- a/core/res/res/drawable/ic_mic.xml +++ b/core/res/res/drawable/ic_mic.xml @@ -19,6 +19,9 @@ Copyright (C) 2018 The Android Open Source Project android:viewportWidth="24.0" android:viewportHeight="24.0"> <path - android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z" - android:fillColor="#FFFFFF"/> + android:fillColor="#FFFFFFFF" + android:pathData="M12,14c1.66,0 3,-1.34 3,-3V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6C9,12.66 10.34,14 12,14zM11,5c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v6c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V5z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M17,11c0,2.76 -2.24,5 -5,5s-5,-2.24 -5,-5H5c0,3.53 2.61,6.43 6,6.92V21h2v-3.08c3.39,-0.49 6,-3.39 6,-6.92H17z"/> </vector>
\ No newline at end of file diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 946b0e6d43dd..cb26a1a244f0 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -89,7 +89,7 @@ <string name="EmergencyCallWarningSummary" msgid="1899692069750260619">"Շտապ կանչերը հասանելի չեն Wi‑Fi-ի միջոցով"</string> <string name="notification_channel_network_alert" msgid="4427736684338074967">"Ծանուցումներ"</string> <string name="notification_channel_call_forward" msgid="2419697808481833249">"Զանգի վերահասցեավորում"</string> - <string name="notification_channel_emergency_callback" msgid="6686166232265733921">"Շտապ հետկանչի ռեժիմ"</string> + <string name="notification_channel_emergency_callback" msgid="6686166232265733921">"Շտապ հետզանգի ռեժիմ"</string> <string name="notification_channel_mobile_data_status" msgid="4575131690860945836">"Բջջային ինտերնետի կարգավիճակը"</string> <string name="notification_channel_sms" msgid="3441746047346135073">"SMS հաղորդագրություններ"</string> <string name="notification_channel_voice_mail" msgid="3954099424160511919">"Ձայնային փոստի հաղորդագրություններ"</string> @@ -657,7 +657,7 @@ <string name="phoneTypeFaxHome" msgid="2067265972322971467">"Տան ֆաքս"</string> <string name="phoneTypePager" msgid="7582359955394921732">"Փեյջեր"</string> <string name="phoneTypeOther" msgid="1544425847868765990">"Այլ"</string> - <string name="phoneTypeCallback" msgid="2712175203065678206">"Ետզանգ"</string> + <string name="phoneTypeCallback" msgid="2712175203065678206">"Հետզանգ"</string> <string name="phoneTypeCar" msgid="8738360689616716982">"Մեքենա"</string> <string name="phoneTypeCompanyMain" msgid="540434356461478916">"Ընկերության հիմնական"</string> <string name="phoneTypeIsdn" msgid="8022453193171370337">"ISDN"</string> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 2e8c7f9fc402..471170bbe93b 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -55,7 +55,7 @@ <!-- Width of the navigation bar when it is placed vertically on the screen in car mode --> <dimen name="navigation_bar_width_car_mode">96dp</dimen> <!-- Height of notification icons in the status bar --> - <dimen name="status_bar_icon_size">24dip</dimen> + <dimen name="status_bar_icon_size">22dip</dimen> <!-- Size of the giant number (unread count) in the notifications --> <dimen name="status_bar_content_number_size">48sp</dimen> <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. --> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 5b7fc6efee95..fc00f1b93955 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -132,6 +132,7 @@ public class SettingsBackupTest { Settings.Global.BLE_SCAN_LOW_LATENCY_WINDOW_MS, Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS, Settings.Global.BLE_SCAN_BACKGROUND_MODE, + Settings.Global.BLOCKED_SLICES, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, Settings.Global.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX, @@ -155,6 +156,7 @@ public class SettingsBackupTest { Settings.Global.CAPTIVE_PORTAL_HTTP_URL, Settings.Global.CAPTIVE_PORTAL_MODE, Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, + Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS, Settings.Global.CAPTIVE_PORTAL_SERVER, Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, Settings.Global.CAPTIVE_PORTAL_USER_AGENT, @@ -591,6 +593,7 @@ public class SettingsBackupTest { Settings.Secure.SHOW_ROTATION_SUGGESTIONS, Settings.Secure.SKIP_FIRST_USE_HINTS, // candidate? Settings.Secure.SMS_DEFAULT_APPLICATION, + Settings.Secure.THEME_MODE, Settings.Secure.TRUST_AGENTS_INITIALIZED, Settings.Secure.TV_INPUT_CUSTOM_LABELS, Settings.Secure.TV_INPUT_HIDDEN_INPUTS, diff --git a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java index c4d5b0c0358d..890929374159 100644 --- a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java +++ b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java @@ -48,6 +48,11 @@ public class SettingsValidatorsTest { } @Test + public void testNonNegativeIntegerValidator_onNullValue_returnsFalse() { + assertFalse(SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR.validate(null)); + } + + @Test public void testAnyIntegerValidator() { assertTrue(SettingsValidators.ANY_INTEGER_VALIDATOR.validate("1")); assertTrue(SettingsValidators.ANY_INTEGER_VALIDATOR.validate("0")); @@ -56,6 +61,16 @@ public class SettingsValidatorsTest { } @Test + public void testAnyIntegerValidator_onNullValue_returnsFalse() { + assertFalse(SettingsValidators.ANY_INTEGER_VALIDATOR.validate(null)); + } + + @Test + public void testUriValidator_onNullValue_returnsTrue() { + assertTrue(SettingsValidators.URI_VALIDATOR.validate(null)); + } + + @Test public void testComponentNameValidator() { assertTrue(SettingsValidators.COMPONENT_NAME_VALIDATOR.validate( "android/com.android.internal.backup.LocalTransport")); @@ -63,11 +78,33 @@ public class SettingsValidatorsTest { } @Test - public void testComponentNameValidator_onNullValue_doesNotThrow() { + public void testComponentNameValidator_onNullValue_returnsFalse() { assertFalse(SettingsValidators.COMPONENT_NAME_VALIDATOR.validate(null)); } @Test + public void testLenientIpAddressValidator_onNullValue_returnsFalse() { + assertFalse(SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR.validate(null)); + } + + @Test + public void testNullableComponentNameValidator_onValidComponentName_returnsTrue() { + assertTrue(SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR.validate( + "android/com.android.internal.backup.LocalTransport")); + } + + @Test + public void testNullableComponentNameValidator_onInvalidComponentName_returnsFalse() { + assertFalse(SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR.validate( + "rectangle")); + } + + @Test + public void testNullableComponentNameValidator_onNullValue_returnsTrue() { + assertTrue(SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR.validate(null)); + } + + @Test public void testLocaleValidator() { assertTrue(SettingsValidators.LOCALE_VALIDATOR.validate("en_US")); assertTrue(SettingsValidators.LOCALE_VALIDATOR.validate("es")); @@ -75,6 +112,11 @@ public class SettingsValidatorsTest { } @Test + public void testLocaleValidator_onNullValue_returnsFalse() { + assertFalse(SettingsValidators.LOCALE_VALIDATOR.validate(null)); + } + + @Test public void testPackageNameValidator() { assertTrue(SettingsValidators.PACKAGE_NAME_VALIDATOR.validate( "com.google.android")); @@ -84,6 +126,11 @@ public class SettingsValidatorsTest { } @Test + public void testPackageNameValidator_onNullValue_returnsFalse() { + assertFalse(SettingsValidators.PACKAGE_NAME_VALIDATOR.validate(null)); + } + + @Test public void testDiscreteValueValidator() { String[] beerTypes = new String[]{"Ale", "American IPA", "Stout"}; Validator v = new SettingsValidators.DiscreteValueValidator(beerTypes); @@ -94,6 +141,14 @@ public class SettingsValidatorsTest { } @Test + public void testDiscreteValueValidator_onNullValue_returnsFalse() { + String[] discreteTypes = new String[]{"Type1", "Type2"}; + Validator v = new SettingsValidators.DiscreteValueValidator(discreteTypes); + + assertFalse(v.validate(null)); + } + + @Test public void testInclusiveIntegerRangeValidator() { Validator v = new SettingsValidators.InclusiveIntegerRangeValidator(0, 5); assertTrue(v.validate("0")); @@ -104,6 +159,13 @@ public class SettingsValidatorsTest { } @Test + public void testInclusiveIntegerRangeValidator_onNullValue_returnsFalse() { + Validator v = new SettingsValidators.InclusiveIntegerRangeValidator(0, 5); + + assertFalse(v.validate(null)); + } + + @Test public void testInclusiveFloatRangeValidator() { Validator v = new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 5.0f); assertTrue(v.validate("0.0")); @@ -114,6 +176,13 @@ public class SettingsValidatorsTest { } @Test + public void testInclusiveFloatRangeValidator_onNullValue_returnsFalse() { + Validator v = new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 5.0f); + + assertFalse(v.validate(null)); + } + + @Test public void testComponentNameListValidator() { Validator v = new SettingsValidators.ComponentNameListValidator(","); assertTrue(v.validate("android/com.android.internal.backup.LocalTransport," @@ -122,12 +191,30 @@ public class SettingsValidatorsTest { } @Test + public void testComponentNameListValidator_onNullValue_returnsFalse() { + Validator v = new SettingsValidators.ComponentNameListValidator(","); + + assertFalse(v.validate(null)); + } + + @Test public void testPackageNameListValidator() { Validator v = new SettingsValidators.PackageNameListValidator(","); assertTrue(v.validate("com.android.internal.backup.LocalTransport,com.google.android.gms")); assertFalse(v.validate("5com.android.internal.backup.LocalTransport,android")); } + @Test + public void testPackageNameListValidator_onNullValue_returnsFalse() { + Validator v = new SettingsValidators.PackageNameListValidator(","); + + assertFalse(v.validate(null)); + } + + @Test + public void dateFormatValidator_onNullValue_returnsFalse() { + assertFalse(Settings.System.DATE_FORMAT_VALIDATOR.validate(null)); + } @Test public void ensureAllBackedUpSystemSettingsHaveValidators() { diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index bcb7cf393f33..70dc6189c6c2 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -46,6 +46,7 @@ import static android.widget.espresso.TextViewAssertions.doesNotHaveStyledText; import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex; import static android.widget.espresso.TextViewAssertions.hasSelection; +import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -77,6 +78,7 @@ import android.view.ActionMode; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; +import android.view.textclassifier.SelectionEvent; import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassifier; import android.view.textclassifier.TextLinks; @@ -90,6 +92,9 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.List; + /** * Tests the TextView widget from an Activity */ @@ -978,6 +983,30 @@ public class TextViewActivityTest { } @Test + public void testSelectionMetricsLogger_noAbandonAfterCopy() throws Throwable { + final List<SelectionEvent> selectionEvents = new ArrayList<>(); + final TextClassifier classifier = new TextClassifier() { + @Override + public void onSelectionEvent(SelectionEvent event) { + selectionEvents.add(event); + } + }; + final TextView textView = mActivity.findViewById(R.id.textview); + mActivityRule.runOnUiThread(() -> textView.setTextClassifier(classifier)); + mInstrumentation.waitForIdleSync(); + final String text = "andyroid@android.com"; + + onView(withId(R.id.textview)).perform(replaceText(text)); + onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('@'))); + sleepForFloatingToolbarPopup(); + clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.copy)); + mInstrumentation.waitForIdleSync(); + + final SelectionEvent lastEvent = selectionEvents.get(selectionEvents.size() - 1); + assertEquals(SelectionEvent.ACTION_COPY, lastEvent.getEventType()); + } + + @Test public void testPastePlainText_menuAction() { initializeClipboardWithText(TextStyle.STYLED); diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 007cdddd803c..b644cc365feb 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -458,7 +458,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) // The chunk must be at least the size of the string pool header. if (size < sizeof(ResStringPool_header)) { - LOG_ALWAYS_FATAL("Bad string block: data size %zu is too small to be a string block", size); + ALOGW("Bad string block: data size %zu is too small to be a string block", size); return (mError=BAD_TYPE); } @@ -468,7 +468,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) if (validate_chunk(reinterpret_cast<const ResChunk_header*>(data), sizeof(ResStringPool_header), reinterpret_cast<const uint8_t*>(data) + size, "ResStringPool_header") != NO_ERROR) { - LOG_ALWAYS_FATAL("Bad string block: malformed block dimensions"); + ALOGW("Bad string block: malformed block dimensions"); return (mError=BAD_TYPE); } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 3057501ea726..6c9a01b89977 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -4717,7 +4717,7 @@ public class AudioManager { NativeEventHandlerDelegate delegate = new NativeEventHandlerDelegate(callback, handler); mDeviceCallbacks.put(callback, delegate); - broadcastDeviceListChange(delegate.getHandler()); + broadcastDeviceListChange_sync(delegate.getHandler()); } } } @@ -4836,9 +4836,9 @@ public class AudioManager { /** * Internal method to compute and generate add/remove messages and then send to any - * registered callbacks. + * registered callbacks. Must be called synchronized on mDeviceCallbacks. */ - private void broadcastDeviceListChange(Handler handler) { + private void broadcastDeviceListChange_sync(Handler handler) { int status; // Get the new current set of ports @@ -4860,20 +4860,18 @@ public class AudioManager { AudioDeviceInfo[] removed_devices = calcListDeltas(current_ports, mPreviousPorts, GET_DEVICES_ALL); if (added_devices.length != 0 || removed_devices.length != 0) { - synchronized (mDeviceCallbacks) { - for (int i = 0; i < mDeviceCallbacks.size(); i++) { - handler = mDeviceCallbacks.valueAt(i).getHandler(); - if (handler != null) { - if (removed_devices.length != 0) { - handler.sendMessage(Message.obtain(handler, - MSG_DEVICES_DEVICES_REMOVED, - removed_devices)); - } - if (added_devices.length != 0) { - handler.sendMessage(Message.obtain(handler, - MSG_DEVICES_DEVICES_ADDED, - added_devices)); - } + for (int i = 0; i < mDeviceCallbacks.size(); i++) { + handler = mDeviceCallbacks.valueAt(i).getHandler(); + if (handler != null) { + if (removed_devices.length != 0) { + handler.sendMessage(Message.obtain(handler, + MSG_DEVICES_DEVICES_REMOVED, + removed_devices)); + } + if (added_devices.length != 0) { + handler.sendMessage(Message.obtain(handler, + MSG_DEVICES_DEVICES_ADDED, + added_devices)); } } } @@ -4889,7 +4887,9 @@ public class AudioManager { private class OnAmPortUpdateListener implements AudioManager.OnAudioPortUpdateListener { static final String TAG = "OnAmPortUpdateListener"; public void onAudioPortListUpdate(AudioPort[] portList) { - broadcastDeviceListChange(null); + synchronized (mDeviceCallbacks) { + broadcastDeviceListChange_sync(null); + } } /** @@ -4903,7 +4903,9 @@ public class AudioManager { * Callback method called when the mediaserver dies */ public void onServiceDied() { - broadcastDeviceListChange(null); + synchronized (mDeviceCallbacks) { + broadcastDeviceListChange_sync(null); + } } } diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java index 9828275e4a9f..a768dd3faf7b 100644 --- a/media/java/android/media/Image.java +++ b/media/java/android/media/Image.java @@ -207,8 +207,9 @@ public abstract class Image implements AutoCloseable { * after {@link Image#close Image.close()} has been called. * </p> * @return the HardwareBuffer associated with this Image or null if this Image doesn't support - * this feature (e.g. {@link android.media.ImageWriter ImageWriter} or - * {@link android.media.MediaCodec MediaCodec} don't). + * this feature. (Unsupported use cases include Image instances obtained through + * {@link android.media.MediaCodec MediaCodec}, and on versions prior to Android P, + * {@link android.media.ImageWriter ImageWriter}). */ @Nullable public HardwareBuffer getHardwareBuffer() { diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java index 397768af84d1..4c0153f70192 100644 --- a/media/java/android/media/ImageWriter.java +++ b/media/java/android/media/ImageWriter.java @@ -20,6 +20,7 @@ import android.graphics.ImageFormat; import android.graphics.PixelFormat; import android.graphics.Rect; import android.hardware.camera2.utils.SurfaceUtils; +import android.hardware.HardwareBuffer; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -58,12 +59,17 @@ import java.util.concurrent.CopyOnWriteArrayList; * </p> * <p> * If the application already has an Image from {@link ImageReader}, the - * application can directly queue this Image into ImageWriter (via - * {@link #queueInputImage}), potentially with zero buffer copies. For the - * {@link ImageFormat#PRIVATE PRIVATE} format Images produced by - * {@link ImageReader}, this is the only way to send Image data to ImageWriter, - * as the Image data aren't accessible by the application. + * application can directly queue this Image into the ImageWriter (via + * {@link #queueInputImage}), potentially with zero buffer copies. This + * even works if the image format of the ImageWriter is + * {@link ImageFormat#PRIVATE PRIVATE}, and prior to Android P is the only + * way to enqueue images into such an ImageWriter. Starting in Android P + * private images may also be accessed through their hardware buffers + * (when available) through the {@link Image#getHardwareBuffer()} method. + * Attempting to access the planes of a private image, will return an + * empty array. * </p> + * <p> * Once new input Images are queued into an ImageWriter, it's up to the * downstream components (e.g. {@link ImageReader} or * {@link android.hardware.camera2.CameraDevice}) to consume the Images. If the @@ -257,29 +263,26 @@ public class ImageWriter implements AutoCloseable { * <p> * If the format of ImageWriter is {@link ImageFormat#PRIVATE PRIVATE} ( * {@link ImageWriter#getFormat()} == {@link ImageFormat#PRIVATE}), the - * image buffer is inaccessible to the application, and calling this method - * will result in an {@link IllegalStateException}. Instead, the application - * should acquire images from some other component (e.g. an + * image buffer is accessible to the application only through the hardware + * buffer obtained through {@link Image#getHardwareBuffer()}. (On Android + * versions prior to P, dequeueing private buffers will cause an + * {@link IllegalStateException} to be thrown). Alternatively, + * the application can acquire images from some other component (e.g. an * {@link ImageReader}), and queue them directly to this ImageWriter via the * {@link ImageWriter#queueInputImage queueInputImage()} method. * </p> * * @return The next available input Image from this ImageWriter. * @throws IllegalStateException if {@code maxImages} Images are currently - * dequeued, or the ImageWriter format is - * {@link ImageFormat#PRIVATE PRIVATE}, or the input - * {@link android.view.Surface Surface} has been abandoned by the - * consumer component that provided the {@link android.view.Surface Surface}. + * dequeued, or the input {@link android.view.Surface Surface} + * has been abandoned by the consumer component that provided + * the {@link android.view.Surface Surface}. Prior to Android + * P, throws if the ImageWriter format is + * {@link ImageFormat#PRIVATE PRIVATE}. * @see #queueInputImage * @see Image#close */ public Image dequeueInputImage() { - if (mWriterFormat == ImageFormat.PRIVATE) { - throw new IllegalStateException( - "PRIVATE format ImageWriter doesn't support this operation since the images are" - + " inaccessible to the application!"); - } - if (mDequeuedImages.size() >= mMaxImages) { throw new IllegalStateException("Already dequeued max number of Images " + mMaxImages); } @@ -743,6 +746,13 @@ public class ImageWriter implements AutoCloseable { } @Override + public HardwareBuffer getHardwareBuffer() { + throwISEIfImageIsInvalid(); + + return nativeGetHardwareBuffer(); + } + + @Override public Plane[] getPlanes() { throwISEIfImageIsInvalid(); @@ -863,6 +873,8 @@ public class ImageWriter implements AutoCloseable { private synchronized native int nativeGetHeight(); private synchronized native int nativeGetFormat(); + + private synchronized native HardwareBuffer nativeGetHardwareBuffer(); } // Native implemented ImageWriter methods. diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 392a1eb01d30..ada91be9ca4d 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -2848,8 +2848,12 @@ public class MediaPlayer extends PlayerBase mInbandTrackIndices.set(i); } + if (tracks[i] == null) { + Log.w(TAG, "unexpected NULL track at index " + i); + } // newly appeared inband track - if (tracks[i].getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) { + if (tracks[i] != null + && tracks[i].getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) { SubtitleTrack track = mSubtitleController.addTrack( tracks[i].getFormat()); mIndexTrackPairs.add(Pair.create(i, track)); diff --git a/media/java/android/media/audiofx/DynamicsProcessing.java b/media/java/android/media/audiofx/DynamicsProcessing.java index 4c17ae1d93b3..1c3cff9cdb3a 100644 --- a/media/java/android/media/audiofx/DynamicsProcessing.java +++ b/media/java/android/media/audiofx/DynamicsProcessing.java @@ -279,25 +279,24 @@ public final class DynamicsProcessing extends AudioEffect { private static final boolean PREEQ_DEFAULT_ENABLED = true; private static final boolean POSTEQ_DEFAULT_ENABLED = true; - private static final boolean MBC_DEFAULT_ENABLED = true; - private static final float MBC_DEFAULT_ATTACK_TIME = 50; // ms - private static final float MBC_DEFAULT_RELEASE_TIME = 120; // ms - private static final float MBC_DEFAULT_RATIO = 2; // 1:N - private static final float MBC_DEFAULT_THRESHOLD = -30; // dB + private static final float MBC_DEFAULT_ATTACK_TIME = 3; // ms + private static final float MBC_DEFAULT_RELEASE_TIME = 80; // ms + private static final float MBC_DEFAULT_RATIO = 1; // N:1 + private static final float MBC_DEFAULT_THRESHOLD = -45; // dB private static final float MBC_DEFAULT_KNEE_WIDTH = 0; // dB - private static final float MBC_DEFAULT_NOISE_GATE_THRESHOLD = -80; // dB - private static final float MBC_DEFAULT_EXPANDER_RATIO = 1.5f; // N:1 + private static final float MBC_DEFAULT_NOISE_GATE_THRESHOLD = -90; // dB + private static final float MBC_DEFAULT_EXPANDER_RATIO = 1; // 1:N private static final float MBC_DEFAULT_PRE_GAIN = 0; // dB - private static final float MBC_DEFAULT_POST_GAIN = 10; // dB + private static final float MBC_DEFAULT_POST_GAIN = 0; // dB private static final boolean LIMITER_DEFAULT_ENABLED = true; private static final int LIMITER_DEFAULT_LINK_GROUP = 0;//; - private static final float LIMITER_DEFAULT_ATTACK_TIME = 50; // ms - private static final float LIMITER_DEFAULT_RELEASE_TIME = 120; // ms - private static final float LIMITER_DEFAULT_RATIO = 2; // 1:N - private static final float LIMITER_DEFAULT_THRESHOLD = -30; // dB - private static final float LIMITER_DEFAULT_POST_GAIN = 10; // dB + private static final float LIMITER_DEFAULT_ATTACK_TIME = 1; // ms + private static final float LIMITER_DEFAULT_RELEASE_TIME = 60; // ms + private static final float LIMITER_DEFAULT_RATIO = 10; // N:1 + private static final float LIMITER_DEFAULT_THRESHOLD = -2; // dB + private static final float LIMITER_DEFAULT_POST_GAIN = 0; // dB private static final float DEFAULT_MIN_FREQUENCY = 220; // Hz private static final float DEFAULT_MAX_FREQUENCY = 20000; // Hz @@ -540,13 +539,14 @@ public final class DynamicsProcessing extends AudioEffect { * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on. * @param attackTime Attack Time for compressor in milliseconds (ms) * @param releaseTime Release Time for compressor in milliseconds (ms) - * @param ratio Compressor ratio (1:N) + * @param ratio Compressor ratio (N:1) (input:output) * @param threshold Compressor threshold measured in decibels (dB) from 0 dB Full Scale * (dBFS). * @param kneeWidth Width in decibels (dB) around compressor threshold point. * @param noiseGateThreshold Noise gate threshold in decibels (dB) from 0 dB Full Scale * (dBFS). - * @param expanderRatio Expander ratio (N:1) for signals below the Noise Gate Threshold. + * @param expanderRatio Expander ratio (1:N) (input:output) for signals below the Noise Gate + * Threshold. * @param preGain Gain applied to the signal BEFORE the compression. * @param postGain Gain applied to the signal AFTER compression. */ @@ -618,13 +618,13 @@ public final class DynamicsProcessing extends AudioEffect { */ public void setReleaseTime(float releaseTime) { mReleaseTime = releaseTime; } /** - * gets the compressor ratio (1:N) - * @return compressor ratio (1:N) + * gets the compressor ratio (N:1) + * @return compressor ratio (N:1) */ public float getRatio() { return mRatio; } /** - * sets compressor ratio (1:N) - * @param ratio desired for the compressor (1:N) + * sets compressor ratio (N:1) + * @param ratio desired for the compressor (N:1) */ public void setRatio(float ratio) { mRatio = ratio; } /** @@ -670,13 +670,13 @@ public final class DynamicsProcessing extends AudioEffect { public void setNoiseGateThreshold(float noiseGateThreshold) { mNoiseGateThreshold = noiseGateThreshold; } /** - * gets Expander ratio (N:1) for signals below the Noise Gate Threshold. - * @return Expander ratio (N:1) + * gets Expander ratio (1:N) for signals below the Noise Gate Threshold. + * @return Expander ratio (1:N) */ public float getExpanderRatio() { return mExpanderRatio; } /** - * sets Expander ratio (N:1) for signals below the Noise Gate Threshold. - * @param expanderRatio desired expander ratio (N:1) + * sets Expander ratio (1:N) for signals below the Noise Gate Threshold. + * @param expanderRatio desired expander ratio (1:N) */ public void setExpanderRatio(float expanderRatio) { mExpanderRatio = expanderRatio; } /** @@ -909,7 +909,7 @@ public final class DynamicsProcessing extends AudioEffect { * same linkGroup index will react together. * @param attackTime Attack Time for limiter compressor in milliseconds (ms) * @param releaseTime Release Time for limiter compressor in milliseconds (ms) - * @param ratio Limiter Compressor ratio (1:N) + * @param ratio Limiter Compressor ratio (N:1) (input:output) * @param threshold Limiter Compressor threshold measured in decibels (dB) from 0 dB Full * Scale (dBFS). * @param postGain Gain applied to the signal AFTER compression. @@ -985,13 +985,13 @@ public final class DynamicsProcessing extends AudioEffect { */ public void setReleaseTime(float releaseTime) { mReleaseTime = releaseTime; } /** - * gets the limiter compressor ratio (1:N) - * @return limiter compressor ratio (1:N) + * gets the limiter compressor ratio (N:1) + * @return limiter compressor ratio (N:1) */ public float getRatio() { return mRatio; } /** - * sets limiter compressor ratio (1:N) - * @param ratio desired for the limiter compressor (1:N) + * sets limiter compressor ratio (N:1) + * @param ratio desired for the limiter compressor (N:1) */ public void setRatio(float ratio) { mRatio = ratio; } /** diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl index 9634c7fe257b..626338d97ccb 100644 --- a/media/java/android/media/session/ISessionCallback.aidl +++ b/media/java/android/media/session/ISessionCallback.aidl @@ -17,6 +17,7 @@ package android.media.session; import android.content.Intent; import android.media.Rating; +import android.media.session.ISessionControllerCallback; import android.net.Uri; import android.os.Bundle; import android.os.ResultReceiver; @@ -25,33 +26,46 @@ import android.os.ResultReceiver; * @hide */ oneway interface ISessionCallback { - void onCommand(String packageName, int pid, int uid, String command, in Bundle args, - in ResultReceiver cb); + void onCommand(String packageName, int pid, int uid, ISessionControllerCallback caller, + String command, in Bundle args, in ResultReceiver cb); void onMediaButton(String packageName, int pid, int uid, in Intent mediaButtonIntent, int sequenceNumber, in ResultReceiver cb); + void onMediaButtonFromController(String packageName, int pid, int uid, + ISessionControllerCallback caller, in Intent mediaButtonIntent); // These callbacks are for the TransportPerformer - void onPrepare(String packageName, int pid, int uid); - void onPrepareFromMediaId(String packageName, int pid, int uid, String mediaId, - in Bundle extras); - void onPrepareFromSearch(String packageName, int pid, int uid, String query, in Bundle extras); - void onPrepareFromUri(String packageName, int pid, int uid, in Uri uri, in Bundle extras); - void onPlay(String packageName, int pid, int uid); - void onPlayFromMediaId(String packageName, int pid, int uid, String mediaId, in Bundle extras); - void onPlayFromSearch(String packageName, int pid, int uid, String query, in Bundle extras); - void onPlayFromUri(String packageName, int pid, int uid, in Uri uri, in Bundle extras); - void onSkipToTrack(String packageName, int pid, int uid, long id); - void onPause(String packageName, int pid, int uid); - void onStop(String packageName, int pid, int uid); - void onNext(String packageName, int pid, int uid); - void onPrevious(String packageName, int pid, int uid); - void onFastForward(String packageName, int pid, int uid); - void onRewind(String packageName, int pid, int uid); - void onSeekTo(String packageName, int pid, int uid, long pos); - void onRate(String packageName, int pid, int uid, in Rating rating); - void onCustomAction(String packageName, int pid, int uid, String action, in Bundle args); + void onPrepare(String packageName, int pid, int uid, ISessionControllerCallback caller); + void onPrepareFromMediaId(String packageName, int pid, int uid, + ISessionControllerCallback caller, String mediaId, in Bundle extras); + void onPrepareFromSearch(String packageName, int pid, int uid, + ISessionControllerCallback caller, String query, in Bundle extras); + void onPrepareFromUri(String packageName, int pid, int uid, ISessionControllerCallback caller, + in Uri uri, in Bundle extras); + void onPlay(String packageName, int pid, int uid, ISessionControllerCallback caller); + void onPlayFromMediaId(String packageName, int pid, int uid, ISessionControllerCallback caller, + String mediaId, in Bundle extras); + void onPlayFromSearch(String packageName, int pid, int uid, ISessionControllerCallback caller, + String query, in Bundle extras); + void onPlayFromUri(String packageName, int pid, int uid, ISessionControllerCallback caller, + in Uri uri, in Bundle extras); + void onSkipToTrack(String packageName, int pid, int uid, ISessionControllerCallback caller, + long id); + void onPause(String packageName, int pid, int uid, ISessionControllerCallback caller); + void onStop(String packageName, int pid, int uid, ISessionControllerCallback caller); + void onNext(String packageName, int pid, int uid, ISessionControllerCallback caller); + void onPrevious(String packageName, int pid, int uid, ISessionControllerCallback caller); + void onFastForward(String packageName, int pid, int uid, ISessionControllerCallback caller); + void onRewind(String packageName, int pid, int uid, ISessionControllerCallback caller); + void onSeekTo(String packageName, int pid, int uid, ISessionControllerCallback caller, + long pos); + void onRate(String packageName, int pid, int uid, ISessionControllerCallback caller, + in Rating rating); + void onCustomAction(String packageName, int pid, int uid, ISessionControllerCallback caller, + String action, in Bundle args); // These callbacks are for volume handling - void onAdjustVolume(String packageName, int pid, int uid, int direction); - void onSetVolumeTo(String packageName, int pid, int uid, int value); + void onAdjustVolume(String packageName, int pid, int uid, ISessionControllerCallback caller, + int direction); + void onSetVolumeTo(String packageName, int pid, int uid, + ISessionControllerCallback caller, int value); } diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl index b4f52f9d00bc..861a8cea7db2 100644 --- a/media/java/android/media/session/ISessionController.aidl +++ b/media/java/android/media/session/ISessionController.aidl @@ -32,42 +32,53 @@ import android.view.KeyEvent; import java.util.List; /** - * Interface to a MediaSession in the system. + * Interface to MediaSessionRecord in the system. * @hide */ interface ISessionController { - void sendCommand(String packageName, String command, in Bundle args, in ResultReceiver cb); - boolean sendMediaButton(String packageName, boolean asSystemService, in KeyEvent mediaButton); - void registerCallbackListener(in ISessionControllerCallback cb); - void unregisterCallbackListener(in ISessionControllerCallback cb); + void sendCommand(String packageName, ISessionControllerCallback caller, + String command, in Bundle args, in ResultReceiver cb); + boolean sendMediaButton(String packageName, ISessionControllerCallback caller, + boolean asSystemService, in KeyEvent mediaButton); + void registerCallbackListener(String packageName, ISessionControllerCallback cb); + void unregisterCallbackListener(ISessionControllerCallback cb); boolean isTransportControlEnabled(); String getPackageName(); String getTag(); PendingIntent getLaunchPendingIntent(); long getFlags(); ParcelableVolumeInfo getVolumeAttributes(); - void adjustVolume(String packageName, boolean asSystemService, int direction, int flags); - void setVolumeTo(String packageName, int value, int flags); + void adjustVolume(String packageName, ISessionControllerCallback caller, + boolean asSystemService, int direction, int flags); + void setVolumeTo(String packageName, ISessionControllerCallback caller, + int value, int flags); // These commands are for the TransportControls - void prepare(String packageName); - void prepareFromMediaId(String packageName, String mediaId, in Bundle extras); - void prepareFromSearch(String packageName, String string, in Bundle extras); - void prepareFromUri(String packageName, in Uri uri, in Bundle extras); - void play(String packageName); - void playFromMediaId(String packageName, String mediaId, in Bundle extras); - void playFromSearch(String packageName, String string, in Bundle extras); - void playFromUri(String packageName, in Uri uri, in Bundle extras); - void skipToQueueItem(String packageName, long id); - void pause(String packageName); - void stop(String packageName); - void next(String packageName); - void previous(String packageName); - void fastForward(String packageName); - void rewind(String packageName); - void seekTo(String packageName, long pos); - void rate(String packageName, in Rating rating); - void sendCustomAction(String packageName, String action, in Bundle args); + void prepare(String packageName, ISessionControllerCallback caller); + void prepareFromMediaId(String packageName, ISessionControllerCallback caller, + String mediaId, in Bundle extras); + void prepareFromSearch(String packageName, ISessionControllerCallback caller, + String string, in Bundle extras); + void prepareFromUri(String packageName, ISessionControllerCallback caller, + in Uri uri, in Bundle extras); + void play(String packageName, ISessionControllerCallback caller); + void playFromMediaId(String packageName, ISessionControllerCallback caller, + String mediaId, in Bundle extras); + void playFromSearch(String packageName, ISessionControllerCallback caller, + String string, in Bundle extras); + void playFromUri(String packageName, ISessionControllerCallback caller, + in Uri uri, in Bundle extras); + void skipToQueueItem(String packageName, ISessionControllerCallback caller, long id); + void pause(String packageName, ISessionControllerCallback caller); + void stop(String packageName, ISessionControllerCallback caller); + void next(String packageName, ISessionControllerCallback caller); + void previous(String packageName, ISessionControllerCallback caller); + void fastForward(String packageName, ISessionControllerCallback caller); + void rewind(String packageName, ISessionControllerCallback caller); + void seekTo(String packageName, ISessionControllerCallback caller, long pos); + void rate(String packageName, ISessionControllerCallback caller, in Rating rating); + void sendCustomAction(String packageName, ISessionControllerCallback caller, + String action, in Bundle args); MediaMetadata getMetadata(); PlaybackState getPlaybackState(); ParceledListSlice getQueue(); diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index 8c34a31b1c8f..de22fa32c0ac 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -126,7 +126,7 @@ public final class MediaController { * @return true if the event was sent to the session, false otherwise. */ public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) { - return dispatchMediButtonEventInternal(false, keyEvent); + return dispatchMediaButtonEventInternal(false, keyEvent); } /** @@ -142,10 +142,10 @@ public final class MediaController { * @hide */ public boolean dispatchMediaButtonEventAsSystemService(@NonNull KeyEvent keyEvent) { - return dispatchMediButtonEventInternal(true, keyEvent); + return dispatchMediaButtonEventInternal(true, keyEvent); } - private boolean dispatchMediButtonEventInternal(boolean asSystemService, + private boolean dispatchMediaButtonEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent) { if (keyEvent == null) { throw new IllegalArgumentException("KeyEvent may not be null"); @@ -154,8 +154,8 @@ public final class MediaController { return false; } try { - return mSessionBinder.sendMediaButton(mContext.getPackageName(), asSystemService, - keyEvent); + return mSessionBinder.sendMediaButton(mContext.getPackageName(), mCbStub, + asSystemService, keyEvent); } catch (RemoteException e) { // System is dead. =( } @@ -189,7 +189,7 @@ public final class MediaController { break; } try { - mSessionBinder.adjustVolume(mContext.getPackageName(), true, direction, + mSessionBinder.adjustVolume(mContext.getPackageName(), mCbStub, true, direction, AudioManager.FLAG_SHOW_UI); } catch (RemoteException e) { Log.wtf(TAG, "Error calling adjustVolumeBy", e); @@ -200,7 +200,7 @@ public final class MediaController { final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE | AudioManager.FLAG_FROM_KEY; try { - mSessionBinder.adjustVolume(mContext.getPackageName(), true, 0, flags); + mSessionBinder.adjustVolume(mContext.getPackageName(), mCbStub, true, 0, flags); } catch (RemoteException e) { Log.wtf(TAG, "Error calling adjustVolumeBy", e); } @@ -369,7 +369,7 @@ public final class MediaController { */ public void setVolumeTo(int value, int flags) { try { - mSessionBinder.setVolumeTo(mContext.getPackageName(), value, flags); + mSessionBinder.setVolumeTo(mContext.getPackageName(), mCbStub, value, flags); } catch (RemoteException e) { Log.wtf(TAG, "Error calling setVolumeTo.", e); } @@ -390,7 +390,8 @@ public final class MediaController { */ public void adjustVolume(int direction, int flags) { try { - mSessionBinder.adjustVolume(mContext.getPackageName(), false, direction, flags); + mSessionBinder.adjustVolume(mContext.getPackageName(), mCbStub, false, direction, + flags); } catch (RemoteException e) { Log.wtf(TAG, "Error calling adjustVolumeBy.", e); } @@ -456,7 +457,7 @@ public final class MediaController { throw new IllegalArgumentException("command cannot be null or empty"); } try { - mSessionBinder.sendCommand(mContext.getPackageName(), command, args, cb); + mSessionBinder.sendCommand(mContext.getPackageName(), mCbStub, command, args, cb); } catch (RemoteException e) { Log.d(TAG, "Dead object in sendCommand.", e); } @@ -521,7 +522,7 @@ public final class MediaController { if (!mCbRegistered) { try { - mSessionBinder.registerCallbackListener(mCbStub); + mSessionBinder.registerCallbackListener(mContext.getPackageName(), mCbStub); mCbRegistered = true; } catch (RemoteException e) { Log.e(TAG, "Dead object in registerCallback", e); @@ -668,7 +669,7 @@ public final class MediaController { */ public void prepare() { try { - mSessionBinder.prepare(mContext.getPackageName()); + mSessionBinder.prepare(mContext.getPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling prepare.", e); } @@ -692,7 +693,8 @@ public final class MediaController { "You must specify a non-empty String for prepareFromMediaId."); } try { - mSessionBinder.prepareFromMediaId(mContext.getPackageName(), mediaId, extras); + mSessionBinder.prepareFromMediaId(mContext.getPackageName(), mCbStub, mediaId, + extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e); } @@ -718,7 +720,7 @@ public final class MediaController { query = ""; } try { - mSessionBinder.prepareFromSearch(mContext.getPackageName(), query, extras); + mSessionBinder.prepareFromSearch(mContext.getPackageName(), mCbStub, query, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling prepare(" + query + ").", e); } @@ -742,7 +744,7 @@ public final class MediaController { "You must specify a non-empty Uri for prepareFromUri."); } try { - mSessionBinder.prepareFromUri(mContext.getPackageName(), uri, extras); + mSessionBinder.prepareFromUri(mContext.getPackageName(), mCbStub, uri, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling prepare(" + uri + ").", e); } @@ -753,7 +755,7 @@ public final class MediaController { */ public void play() { try { - mSessionBinder.play(mContext.getPackageName()); + mSessionBinder.play(mContext.getPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling play.", e); } @@ -772,7 +774,7 @@ public final class MediaController { "You must specify a non-empty String for playFromMediaId."); } try { - mSessionBinder.playFromMediaId(mContext.getPackageName(), mediaId, extras); + mSessionBinder.playFromMediaId(mContext.getPackageName(), mCbStub, mediaId, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling play(" + mediaId + ").", e); } @@ -794,7 +796,7 @@ public final class MediaController { query = ""; } try { - mSessionBinder.playFromSearch(mContext.getPackageName(), query, extras); + mSessionBinder.playFromSearch(mContext.getPackageName(), mCbStub, query, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling play(" + query + ").", e); } @@ -813,7 +815,7 @@ public final class MediaController { "You must specify a non-empty Uri for playFromUri."); } try { - mSessionBinder.playFromUri(mContext.getPackageName(), uri, extras); + mSessionBinder.playFromUri(mContext.getPackageName(), mCbStub, uri, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling play(" + uri + ").", e); } @@ -825,7 +827,7 @@ public final class MediaController { */ public void skipToQueueItem(long id) { try { - mSessionBinder.skipToQueueItem(mContext.getPackageName(), id); + mSessionBinder.skipToQueueItem(mContext.getPackageName(), mCbStub, id); } catch (RemoteException e) { Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e); } @@ -837,7 +839,7 @@ public final class MediaController { */ public void pause() { try { - mSessionBinder.pause(mContext.getPackageName()); + mSessionBinder.pause(mContext.getPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling pause.", e); } @@ -849,7 +851,7 @@ public final class MediaController { */ public void stop() { try { - mSessionBinder.stop(mContext.getPackageName()); + mSessionBinder.stop(mContext.getPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling stop.", e); } @@ -862,7 +864,7 @@ public final class MediaController { */ public void seekTo(long pos) { try { - mSessionBinder.seekTo(mContext.getPackageName(), pos); + mSessionBinder.seekTo(mContext.getPackageName(), mCbStub, pos); } catch (RemoteException e) { Log.wtf(TAG, "Error calling seekTo.", e); } @@ -874,7 +876,7 @@ public final class MediaController { */ public void fastForward() { try { - mSessionBinder.fastForward(mContext.getPackageName()); + mSessionBinder.fastForward(mContext.getPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling fastForward.", e); } @@ -885,7 +887,7 @@ public final class MediaController { */ public void skipToNext() { try { - mSessionBinder.next(mContext.getPackageName()); + mSessionBinder.next(mContext.getPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling next.", e); } @@ -897,7 +899,7 @@ public final class MediaController { */ public void rewind() { try { - mSessionBinder.rewind(mContext.getPackageName()); + mSessionBinder.rewind(mContext.getPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling rewind.", e); } @@ -908,7 +910,7 @@ public final class MediaController { */ public void skipToPrevious() { try { - mSessionBinder.previous(mContext.getPackageName()); + mSessionBinder.previous(mContext.getPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling previous.", e); } @@ -923,7 +925,7 @@ public final class MediaController { */ public void setRating(Rating rating) { try { - mSessionBinder.rate(mContext.getPackageName(), rating); + mSessionBinder.rate(mContext.getPackageName(), mCbStub, rating); } catch (RemoteException e) { Log.wtf(TAG, "Error calling rate.", e); } @@ -937,7 +939,7 @@ public final class MediaController { * custom action. */ public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction, - @Nullable Bundle args) { + @Nullable Bundle args) { if (customAction == null) { throw new IllegalArgumentException("CustomAction cannot be null."); } @@ -958,7 +960,7 @@ public final class MediaController { throw new IllegalArgumentException("CustomAction cannot be null."); } try { - mSessionBinder.sendCustomAction(mContext.getPackageName(), action, args); + mSessionBinder.sendCustomAction(mContext.getPackageName(), mCbStub, action, args); } catch (RemoteException e) { Log.d(TAG, "Dead object in sendCustomAction.", e); } @@ -1124,8 +1126,8 @@ public final class MediaController { public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) { MediaController controller = mController.get(); if (controller != null) { - PlaybackInfo info = new PlaybackInfo(pvi.volumeType, pvi.audioAttrs, pvi.controlType, - pvi.maxVolume, pvi.currentVolume); + PlaybackInfo info = new PlaybackInfo(pvi.volumeType, pvi.audioAttrs, + pvi.controlType, pvi.maxVolume, pvi.currentVolume); controller.postMessage(MSG_UPDATE_VOLUME, info, null); } } diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 6f4f20ef780e..fad7e3f55862 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -43,6 +43,7 @@ import android.media.session.MediaSessionManager.RemoteUserInfo; import android.service.media.MediaBrowserService; import android.text.TextUtils; import android.util.Log; +import android.util.Pair; import android.view.KeyEvent; import android.view.ViewConfiguration; @@ -122,15 +123,6 @@ public final class MediaSession { FLAG_EXCLUSIVE_GLOBAL_PRIORITY }) public @interface SessionFlags { } - private static final String EXTRA_KEY_CALLING_PACKAGE = - "android.media.session.extra.CALLING_PACKAGE"; - private static final String EXTRA_KEY_CALLING_PID = - "android.media.session.extra.CALLING_PID"; - private static final String EXTRA_KEY_CALLING_UID = - "android.media.session.extra.CALLING_UID"; - private static final String EXTRA_KEY_ORIGINAL_BUNDLE = - "android.media.session.extra.ORIGINAL_BUNDLE"; - private final Object mLock = new Object(); private final int mMaxBitmapSize; @@ -529,15 +521,11 @@ public final class MediaSession { * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo) */ public final @NonNull RemoteUserInfo getCurrentControllerInfo() { - return createRemoteUserInfo(getCurrentData()); - } - - private @NonNull Bundle getCurrentData() { - if (mCallback == null || mCallback.mCurrentData == null) { + if (mCallback == null || mCallback.mCurrentControllerInfo == null) { throw new IllegalStateException( "This should be called inside of MediaSession.Callback methods"); } - return mCallback.mCurrentData; + return mCallback.mCurrentControllerInfo; } /** @@ -568,161 +556,122 @@ public final class MediaSession { * @hide */ public String getCallingPackage() { - if (mCallback != null) { - return createRemoteUserInfo(mCallback.mCurrentData).getPackageName(); + if (mCallback != null && mCallback.mCurrentControllerInfo != null) { + return mCallback.mCurrentControllerInfo.getPackageName(); } return null; } - private void dispatchPrepare(Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_PREPARE, null, extras); + private void dispatchPrepare(RemoteUserInfo caller) { + postToCallback(caller, CallbackMessageHandler.MSG_PREPARE, null, null); } - private void dispatchPrepareFromMediaId(String mediaId, Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_PREPARE_MEDIA_ID, mediaId, extras); + private void dispatchPrepareFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) { + postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_MEDIA_ID, mediaId, extras); } - private void dispatchPrepareFromSearch(String query, Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_PREPARE_SEARCH, query, extras); + private void dispatchPrepareFromSearch(RemoteUserInfo caller, String query, Bundle extras) { + postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_SEARCH, query, extras); } - private void dispatchPrepareFromUri(Uri uri, Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_PREPARE_URI, uri, extras); + private void dispatchPrepareFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) { + postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_URI, uri, extras); } - private void dispatchPlay(Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_PLAY, null, extras); + private void dispatchPlay(RemoteUserInfo caller) { + postToCallback(caller, CallbackMessageHandler.MSG_PLAY, null, null); } - private void dispatchPlayFromMediaId(String mediaId, Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_PLAY_MEDIA_ID, mediaId, extras); + private void dispatchPlayFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) { + postToCallback(caller, CallbackMessageHandler.MSG_PLAY_MEDIA_ID, mediaId, extras); } - private void dispatchPlayFromSearch(String query, Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_PLAY_SEARCH, query, extras); + private void dispatchPlayFromSearch(RemoteUserInfo caller, String query, Bundle extras) { + postToCallback(caller, CallbackMessageHandler.MSG_PLAY_SEARCH, query, extras); } - private void dispatchPlayFromUri(Uri uri, Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_PLAY_URI, uri, extras); + private void dispatchPlayFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) { + postToCallback(caller, CallbackMessageHandler.MSG_PLAY_URI, uri, extras); } - private void dispatchSkipToItem(long id, Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_SKIP_TO_ITEM, id, extras); + private void dispatchSkipToItem(RemoteUserInfo caller, long id) { + postToCallback(caller, CallbackMessageHandler.MSG_SKIP_TO_ITEM, id, null); } - private void dispatchPause(Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_PAUSE, null, extras); + private void dispatchPause(RemoteUserInfo caller) { + postToCallback(caller, CallbackMessageHandler.MSG_PAUSE, null, null); } - private void dispatchStop(Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_STOP, null, extras); + private void dispatchStop(RemoteUserInfo caller) { + postToCallback(caller, CallbackMessageHandler.MSG_STOP, null, null); } - private void dispatchNext(Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_NEXT, null, extras); + private void dispatchNext(RemoteUserInfo caller) { + postToCallback(caller, CallbackMessageHandler.MSG_NEXT, null, null); } - private void dispatchPrevious(Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_PREVIOUS, null, extras); + private void dispatchPrevious(RemoteUserInfo caller) { + postToCallback(caller, CallbackMessageHandler.MSG_PREVIOUS, null, null); } - private void dispatchFastForward(Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_FAST_FORWARD, null, extras); + private void dispatchFastForward(RemoteUserInfo caller) { + postToCallback(caller, CallbackMessageHandler.MSG_FAST_FORWARD, null, null); } - private void dispatchRewind(Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_REWIND, null, extras); + private void dispatchRewind(RemoteUserInfo caller) { + postToCallback(caller, CallbackMessageHandler.MSG_REWIND, null, null); } - private void dispatchSeekTo(long pos, Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_SEEK_TO, pos, extras); + private void dispatchSeekTo(RemoteUserInfo caller, long pos) { + postToCallback(caller, CallbackMessageHandler.MSG_SEEK_TO, pos, null); } - private void dispatchRate(Rating rating, Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_RATE, rating, extras); + private void dispatchRate(RemoteUserInfo caller, Rating rating) { + postToCallback(caller, CallbackMessageHandler.MSG_RATE, rating, null); } - private void dispatchCustomAction(String action, Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_CUSTOM_ACTION, action, extras); + private void dispatchCustomAction(RemoteUserInfo caller, String action, Bundle args) { + postToCallback(caller, CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args); } - private void dispatchMediaButton(Intent mediaButtonIntent, Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent, extras); + private void dispatchMediaButton(RemoteUserInfo caller, Intent mediaButtonIntent) { + postToCallback(caller, CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent, null); } - private void dispatchAdjustVolume(int direction, Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_ADJUST_VOLUME, direction, extras); + private void dispatchMediaButtonDelayed(RemoteUserInfo info, Intent mediaButtonIntent, + long delay) { + postToCallbackDelayed(info, CallbackMessageHandler.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT, + mediaButtonIntent, null, delay); } - private void dispatchSetVolumeTo(int volume, Bundle extras) { - postToCallback(CallbackMessageHandler.MSG_SET_VOLUME, volume, extras); + private void dispatchAdjustVolume(RemoteUserInfo caller, int direction) { + postToCallback(caller, CallbackMessageHandler.MSG_ADJUST_VOLUME, direction, null); } - private void postCommand(String command, Bundle args, ResultReceiver resultCb, Bundle extras) { + private void dispatchSetVolumeTo(RemoteUserInfo caller, int volume) { + postToCallback(caller, CallbackMessageHandler.MSG_SET_VOLUME, volume, null); + } + + private void dispatchCommand(RemoteUserInfo caller, String command, Bundle args, + ResultReceiver resultCb) { Command cmd = new Command(command, args, resultCb); - postToCallback(CallbackMessageHandler.MSG_COMMAND, cmd, extras); + postToCallback(caller, CallbackMessageHandler.MSG_COMMAND, cmd, null); } - private void postToCallback(int what, Object obj, Bundle extras) { + private void postToCallback(RemoteUserInfo caller, int what, Object obj, Bundle data) { + postToCallbackDelayed(caller, what, obj, data, 0); + } + + private void postToCallbackDelayed(RemoteUserInfo caller, int what, Object obj, Bundle data, + long delay) { synchronized (mLock) { if (mCallback != null) { - mCallback.post(what, obj, extras); + mCallback.post(caller, what, obj, data, delay); } } } /** - * Creates the extra bundle that includes the caller information. - * - * @return An extraBundle that contains caller information - */ - private static Bundle createExtraBundle(String packageName, int pid, int uid) { - return createExtraBundle(packageName, pid, uid, null); - } - - /** - * Creates the extra bundle that includes the caller information. - * - * @param originalBundle bundle - * @return An extraBundle that contains caller information - */ - private static Bundle createExtraBundle(String packageName, int pid, int uid, - Bundle originalBundle) { - Bundle bundle = new Bundle(); - bundle.putString(EXTRA_KEY_CALLING_PACKAGE, packageName); - bundle.putInt(EXTRA_KEY_CALLING_PID, pid); - bundle.putInt(EXTRA_KEY_CALLING_UID, uid); - if (originalBundle != null) { - bundle.putBundle(EXTRA_KEY_ORIGINAL_BUNDLE, originalBundle); - } - return bundle; - } - - /** - * Creates the {@link RemoteUserInfo} from the extra bundle created by - * {@link #createExtraBundle}. - * - * @param extraBundle that previously created by createExtraBundle() - * @return a RemoteUserInfo - */ - private static RemoteUserInfo createRemoteUserInfo(Bundle extraBundle) { - return new RemoteUserInfo( - extraBundle.getString(EXTRA_KEY_CALLING_PACKAGE), - extraBundle.getInt(EXTRA_KEY_CALLING_PID, INVALID_PID), - extraBundle.getInt(EXTRA_KEY_CALLING_UID, INVALID_UID)); - } - - /** - * Gets the original bundle from the extra bundle created by {@link #createExtraBundle}. - * - * @param extraBundle that previously created by createExtraBundle() - * @return a Bundle - */ - private static Bundle getOriginalBundle(Bundle extraBundle) { - return extraBundle.getBundle(EXTRA_KEY_ORIGINAL_BUNDLE); - } - - /** * Return true if this is considered an active playback state. * * @hide @@ -872,10 +821,9 @@ public final class MediaSession { } } else { mMediaPlayPauseKeyPending = true; - mHandler.postDelayed(CallbackMessageHandler - .MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT, - mSession.getCurrentData(), - ViewConfiguration.getDoubleTapTimeout()); + mSession.dispatchMediaButtonDelayed( + mSession.getCurrentControllerInfo(), + mediaButtonIntent, ViewConfiguration.getDoubleTapTimeout()); } return true; default: @@ -1109,12 +1057,19 @@ public final class MediaSession { mMediaSession = new WeakReference<>(session); } + private static RemoteUserInfo createRemoteUserInfo(String packageName, int pid, int uid, + ISessionControllerCallback caller) { + return new RemoteUserInfo(packageName, pid, uid, + caller != null ? caller.asBinder() : null); + } + @Override - public void onCommand(String packageName, int pid, int uid, String command, Bundle args, - ResultReceiver cb) { + public void onCommand(String packageName, int pid, int uid, + ISessionControllerCallback caller, String command, Bundle args, ResultReceiver cb) { MediaSession session = mMediaSession.get(); if (session != null) { - session.postCommand(command, args, cb, createExtraBundle(packageName, pid, uid)); + session.dispatchCommand(createRemoteUserInfo(packageName, pid, uid, caller), + command, args, cb); } } @@ -1124,8 +1079,8 @@ public final class MediaSession { MediaSession session = mMediaSession.get(); try { if (session != null) { - session.dispatchMediaButton( - mediaButtonIntent, createExtraBundle(packageName, pid, uid)); + session.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid, null), + mediaButtonIntent); } } finally { if (cb != null) { @@ -1135,173 +1090,205 @@ public final class MediaSession { } @Override - public void onPrepare(String packageName, int pid, int uid) { + public void onMediaButtonFromController(String packageName, int pid, int uid, + ISessionControllerCallback caller, Intent mediaButtonIntent) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchPrepare(createExtraBundle(packageName, pid, uid)); + session.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid, caller), + mediaButtonIntent); } } @Override - public void onPrepareFromMediaId(String packageName, int pid, int uid, String mediaId, + public void onPrepare(String packageName, int pid, int uid, + ISessionControllerCallback caller) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchPrepare(createRemoteUserInfo(packageName, pid, uid, caller)); + } + } + + @Override + public void onPrepareFromMediaId(String packageName, int pid, int uid, + ISessionControllerCallback caller, String mediaId, Bundle extras) { MediaSession session = mMediaSession.get(); if (session != null) { session.dispatchPrepareFromMediaId( - mediaId, createExtraBundle(packageName, pid, uid, extras)); + createRemoteUserInfo(packageName, pid, uid, caller), mediaId, extras); } } @Override - public void onPrepareFromSearch(String packageName, int pid, int uid, String query, + public void onPrepareFromSearch(String packageName, int pid, int uid, + ISessionControllerCallback caller, String query, Bundle extras) { MediaSession session = mMediaSession.get(); if (session != null) { session.dispatchPrepareFromSearch( - query, createExtraBundle(packageName, pid, uid, extras)); + createRemoteUserInfo(packageName, pid, uid, caller), query, extras); } } @Override - public void onPrepareFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) { + public void onPrepareFromUri(String packageName, int pid, int uid, + ISessionControllerCallback caller, Uri uri, Bundle extras) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchPrepareFromUri(uri, - createExtraBundle(packageName, pid, uid, extras)); + session.dispatchPrepareFromUri(createRemoteUserInfo(packageName, pid, uid, caller), + uri, extras); } } @Override - public void onPlay(String packageName, int pid, int uid) { + public void onPlay(String packageName, int pid, int uid, + ISessionControllerCallback caller) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchPlay(createExtraBundle(packageName, pid, uid)); + session.dispatchPlay(createRemoteUserInfo(packageName, pid, uid, caller)); } } @Override - public void onPlayFromMediaId(String packageName, int pid, int uid, String mediaId, + public void onPlayFromMediaId(String packageName, int pid, int uid, + ISessionControllerCallback caller, String mediaId, Bundle extras) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchPlayFromMediaId( - mediaId, createExtraBundle(packageName, pid, uid, extras)); + session.dispatchPlayFromMediaId(createRemoteUserInfo(packageName, pid, uid, caller), + mediaId, extras); } } @Override - public void onPlayFromSearch(String packageName, int pid, int uid, String query, + public void onPlayFromSearch(String packageName, int pid, int uid, + ISessionControllerCallback caller, String query, Bundle extras) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchPlayFromSearch(query, createExtraBundle(packageName, pid, uid, - extras)); + session.dispatchPlayFromSearch(createRemoteUserInfo(packageName, pid, uid, caller), + query, extras); } } @Override - public void onPlayFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) { + public void onPlayFromUri(String packageName, int pid, int uid, + ISessionControllerCallback caller, Uri uri, Bundle extras) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchPlayFromUri(uri, createExtraBundle(packageName, pid, uid, extras)); + session.dispatchPlayFromUri(createRemoteUserInfo(packageName, pid, uid, caller), + uri, extras); } } @Override - public void onSkipToTrack(String packageName, int pid, int uid, long id) { + public void onSkipToTrack(String packageName, int pid, int uid, + ISessionControllerCallback caller, long id) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchSkipToItem(id, createExtraBundle(packageName, pid, uid)); + session.dispatchSkipToItem(createRemoteUserInfo(packageName, pid, uid, caller), id); } } @Override - public void onPause(String packageName, int pid, int uid) { + public void onPause(String packageName, int pid, int uid, + ISessionControllerCallback caller) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchPause(createExtraBundle(packageName, pid, uid)); + session.dispatchPause(createRemoteUserInfo(packageName, pid, uid, caller)); } } @Override - public void onStop(String packageName, int pid, int uid) { + public void onStop(String packageName, int pid, int uid, + ISessionControllerCallback caller) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchStop(createExtraBundle(packageName, pid, uid)); + session.dispatchStop(createRemoteUserInfo(packageName, pid, uid, caller)); } } @Override - public void onNext(String packageName, int pid, int uid) { + public void onNext(String packageName, int pid, int uid, + ISessionControllerCallback caller) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchNext(createExtraBundle(packageName, pid, uid)); + session.dispatchNext(createRemoteUserInfo(packageName, pid, uid, caller)); } } @Override - public void onPrevious(String packageName, int pid, int uid) { + public void onPrevious(String packageName, int pid, int uid, + ISessionControllerCallback caller) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchPrevious(createExtraBundle(packageName, pid, uid)); + session.dispatchPrevious(createRemoteUserInfo(packageName, pid, uid, caller)); } } @Override - public void onFastForward(String packageName, int pid, int uid) { + public void onFastForward(String packageName, int pid, int uid, + ISessionControllerCallback caller) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchFastForward(createExtraBundle(packageName, pid, uid)); + session.dispatchFastForward(createRemoteUserInfo(packageName, pid, uid, caller)); } } @Override - public void onRewind(String packageName, int pid, int uid) { + public void onRewind(String packageName, int pid, int uid, + ISessionControllerCallback caller) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchRewind(createExtraBundle(packageName, pid, uid)); + session.dispatchRewind(createRemoteUserInfo(packageName, pid, uid, caller)); } } @Override - public void onSeekTo(String packageName, int pid, int uid, long pos) { + public void onSeekTo(String packageName, int pid, int uid, + ISessionControllerCallback caller, long pos) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchSeekTo(pos, createExtraBundle(packageName, pid, uid)); + session.dispatchSeekTo(createRemoteUserInfo(packageName, pid, uid, caller), pos); } } @Override - public void onRate(String packageName, int pid, int uid, Rating rating) { + public void onRate(String packageName, int pid, int uid, ISessionControllerCallback caller, + Rating rating) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchRate(rating, createExtraBundle(packageName, pid, uid)); + session.dispatchRate(createRemoteUserInfo(packageName, pid, uid, caller), rating); } } @Override - public void onCustomAction(String packageName, int pid, int uid, String action, - Bundle args) { + public void onCustomAction(String packageName, int pid, int uid, + ISessionControllerCallback caller, String action, Bundle args) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchCustomAction( - action, createExtraBundle(packageName, pid, uid, args)); + session.dispatchCustomAction(createRemoteUserInfo(packageName, pid, uid, caller), + action, args); } } @Override - public void onAdjustVolume(String packageName, int pid, int uid, int direction) { + public void onAdjustVolume(String packageName, int pid, int uid, + ISessionControllerCallback caller, int direction) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchAdjustVolume(direction, createExtraBundle(packageName, pid, uid)); + session.dispatchAdjustVolume(createRemoteUserInfo(packageName, pid, uid, caller), + direction); } } @Override - public void onSetVolumeTo(String packageName, int pid, int uid, int value) { + public void onSetVolumeTo(String packageName, int pid, int uid, + ISessionControllerCallback caller, int value) { MediaSession session = mMediaSession.get(); if (session != null) { - session.dispatchSetVolumeTo(value, createExtraBundle(packageName, pid, uid)); + session.dispatchSetVolumeTo(createRemoteUserInfo(packageName, pid, uid, caller), + value); } } } @@ -1424,7 +1411,6 @@ public final class MediaSession { } private class CallbackMessageHandler extends Handler { - private static final int MSG_COMMAND = 1; private static final int MSG_MEDIA_BUTTON = 2; private static final int MSG_PREPARE = 3; @@ -1450,7 +1436,7 @@ public final class MediaSession { private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 23; private MediaSession.Callback mCallback; - private Bundle mCurrentData; + private RemoteUserInfo mCurrentControllerInfo; public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) { super(looper, null, true); @@ -1458,60 +1444,58 @@ public final class MediaSession { mCallback.mHandler = this; } - public void post(int what, Object obj, Bundle data) { - Message msg = obtainMessage(what, obj); - msg.setData(data); - msg.sendToTarget(); - } - - public void postDelayed(int what, Bundle data, long delayMs) { - Message msg = obtainMessage(what); + public void post(RemoteUserInfo caller, int what, Object obj, Bundle data, long delayMs) { + Pair<RemoteUserInfo, Object> objWithCaller = Pair.create(caller, obj); + Message msg = obtainMessage(what, objWithCaller); msg.setData(data); - sendMessageDelayed(msg, delayMs); + if (delayMs > 0) { + sendMessageDelayed(msg, delayMs); + } else { + sendMessage(msg); + } } @Override public void handleMessage(Message msg) { - VolumeProvider vp; - Bundle data = msg.getData(); - Bundle originalBundle = getOriginalBundle(data); + mCurrentControllerInfo = ((Pair<RemoteUserInfo, Object>) msg.obj).first; - mCurrentData = data; + VolumeProvider vp; + Object obj = ((Pair<RemoteUserInfo, Object>) msg.obj).second; switch (msg.what) { case MSG_COMMAND: - Command cmd = (Command) msg.obj; + Command cmd = (Command) obj; mCallback.onCommand(cmd.command, cmd.extras, cmd.stub); break; case MSG_MEDIA_BUTTON: - mCallback.onMediaButtonEvent((Intent) msg.obj); + mCallback.onMediaButtonEvent((Intent) obj); break; case MSG_PREPARE: mCallback.onPrepare(); break; case MSG_PREPARE_MEDIA_ID: - mCallback.onPrepareFromMediaId((String) msg.obj, originalBundle); + mCallback.onPrepareFromMediaId((String) obj, msg.getData()); break; case MSG_PREPARE_SEARCH: - mCallback.onPrepareFromSearch((String) msg.obj, originalBundle); + mCallback.onPrepareFromSearch((String) obj, msg.getData()); break; case MSG_PREPARE_URI: - mCallback.onPrepareFromUri((Uri) msg.obj, originalBundle); + mCallback.onPrepareFromUri((Uri) obj, msg.getData()); break; case MSG_PLAY: mCallback.onPlay(); break; case MSG_PLAY_MEDIA_ID: - mCallback.onPlayFromMediaId((String) msg.obj, originalBundle); + mCallback.onPlayFromMediaId((String) obj, msg.getData()); break; case MSG_PLAY_SEARCH: - mCallback.onPlayFromSearch((String) msg.obj, originalBundle); + mCallback.onPlayFromSearch((String) obj, msg.getData()); break; case MSG_PLAY_URI: - mCallback.onPlayFromUri((Uri) msg.obj, originalBundle); + mCallback.onPlayFromUri((Uri) obj, msg.getData()); break; case MSG_SKIP_TO_ITEM: - mCallback.onSkipToQueueItem((Long) msg.obj); + mCallback.onSkipToQueueItem((Long) obj); break; case MSG_PAUSE: mCallback.onPause(); @@ -1532,20 +1516,20 @@ public final class MediaSession { mCallback.onRewind(); break; case MSG_SEEK_TO: - mCallback.onSeekTo((Long) msg.obj); + mCallback.onSeekTo((Long) obj); break; case MSG_RATE: - mCallback.onSetRating((Rating) msg.obj); + mCallback.onSetRating((Rating) obj); break; case MSG_CUSTOM_ACTION: - mCallback.onCustomAction((String) msg.obj, originalBundle); + mCallback.onCustomAction((String) obj, msg.getData()); break; case MSG_ADJUST_VOLUME: synchronized (mLock) { vp = mVolumeProvider; } if (vp != null) { - vp.onAdjustVolume((int) msg.obj); + vp.onAdjustVolume((int) obj); } break; case MSG_SET_VOLUME: @@ -1553,14 +1537,14 @@ public final class MediaSession { vp = mVolumeProvider; } if (vp != null) { - vp.onSetVolumeTo((int) msg.obj); + vp.onSetVolumeTo((int) obj); } break; case MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT: mCallback.handleMediaPlayPauseKeySingleTapIfPending(); break; } - mCurrentData = null; + mCurrentControllerInfo = null; } } } diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index f54bfc137703..3f0b6c5276f5 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -30,6 +30,7 @@ import android.media.ISessionTokensListener; import android.media.MediaSession2; import android.media.MediaSessionService2; import android.media.SessionToken2; +import android.media.browse.MediaBrowser; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -818,19 +819,31 @@ public final class MediaSessionManager { /** * Information of a remote user of {@link MediaSession} or {@link MediaBrowserService}. - * This can be used to decide whether the remote user is trusted app. + * This can be used to decide whether the remote user is trusted app, and also differentiate + * caller of {@link MediaSession} and {@link MediaBrowserService} callbacks. + * <p> + * See {@link #equals(Object)} to take a look at how it differentiate media controller. * * @see #isTrustedForMediaControl(RemoteUserInfo) */ public static final class RemoteUserInfo { - private String mPackageName; - private int mPid; - private int mUid; + private final String mPackageName; + private final int mPid; + private final int mUid; + private final IBinder mCallerBinder; + + public RemoteUserInfo(@NonNull String packageName, int pid, int uid) { + this(packageName, pid, uid, null); + } - public RemoteUserInfo(String packageName, int pid, int uid) { + /** + * @hide + */ + public RemoteUserInfo(String packageName, int pid, int uid, IBinder callerBinder) { mPackageName = packageName; mPid = pid; mUid = uid; + mCallerBinder = callerBinder; } /** @@ -854,15 +867,29 @@ public final class MediaSessionManager { return mUid; } + /** + * Returns equality of two RemoteUserInfo. Two RemoteUserInfos are the same only if they're + * sent to the same controller (either {@link MediaController} or + * {@link MediaBrowser}. If it's not nor one of them is triggered by the key presses, they + * would be considered as different one. + * <p> + * If you only want to compare the caller's package, compare them with the + * {@link #getPackageName()}, {@link #getPid()}, and/or {@link #getUid()} directly. + * + * @param obj the reference object with which to compare. + * @return {@code true} if equals, {@code false} otherwise + */ @Override public boolean equals(Object obj) { if (!(obj instanceof RemoteUserInfo)) { return false; } + if (this == obj) { + return true; + } RemoteUserInfo otherUserInfo = (RemoteUserInfo) obj; - return TextUtils.equals(mPackageName, otherUserInfo.mPackageName) - && mPid == otherUserInfo.mPid - && mUid == otherUserInfo.mUid; + return (mCallerBinder == null || otherUserInfo.mCallerBinder == null) ? false + : mCallerBinder.equals(otherUserInfo.mCallerBinder); } @Override diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp index 11659e4826c5..f8f7a90b3937 100644 --- a/media/jni/android_media_ImageWriter.cpp +++ b/media/jni/android_media_ImageWriter.cpp @@ -25,11 +25,14 @@ #include <gui/Surface.h> #include <android_runtime/AndroidRuntime.h> #include <android_runtime/android_view_Surface.h> +#include <android_runtime/android_hardware_HardwareBuffer.h> +#include <private/android/AHardwareBufferHelpers.h> #include <jni.h> #include <nativehelper/JNIHelp.h> #include <stdint.h> #include <inttypes.h> +#include <android/hardware_buffer_jni.h> #define IMAGE_BUFFER_JNI_ID "mNativeBuffer" #define IMAGE_FORMAT_UNKNOWN 0 // This is the same value as ImageFormat#UNKNOWN. @@ -701,6 +704,20 @@ static jint Image_getFormat(JNIEnv* env, jobject thiz) { return static_cast<jint>(publicFmt); } +static jobject Image_getHardwareBuffer(JNIEnv* env, jobject thiz) { + GraphicBuffer* buffer; + Image_getNativeContext(env, thiz, &buffer, NULL); + if (buffer == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", + "Image is not initialized"); + return NULL; + } + AHardwareBuffer* b = AHardwareBuffer_from_GraphicBuffer(buffer); + // don't user the public AHardwareBuffer_toHardwareBuffer() because this would force us + // to link against libandroid.so + return android_hardware_HardwareBuffer_createFromAHardwareBuffer(env, b); +} + static void Image_setFenceFd(JNIEnv* env, jobject thiz, int fenceFd) { ALOGV("%s:", __FUNCTION__); env->SetIntField(thiz, gSurfaceImageClassInfo.mNativeFenceFd, reinterpret_cast<jint>(fenceFd)); @@ -818,10 +835,12 @@ static JNINativeMethod gImageWriterMethods[] = { static JNINativeMethod gImageMethods[] = { {"nativeCreatePlanes", "(II)[Landroid/media/ImageWriter$WriterSurfaceImage$SurfacePlane;", - (void*)Image_createSurfacePlanes }, - {"nativeGetWidth", "()I", (void*)Image_getWidth }, - {"nativeGetHeight", "()I", (void*)Image_getHeight }, - {"nativeGetFormat", "()I", (void*)Image_getFormat }, + (void*)Image_createSurfacePlanes }, + {"nativeGetWidth", "()I", (void*)Image_getWidth }, + {"nativeGetHeight", "()I", (void*)Image_getHeight }, + {"nativeGetFormat", "()I", (void*)Image_getFormat }, + {"nativeGetHardwareBuffer", "()Landroid/hardware/HardwareBuffer;", + (void*)Image_getHardwareBuffer }, }; int register_android_media_ImageWriter(JNIEnv *env) { diff --git a/packages/SettingsLib/res/values-as/arrays.xml b/packages/SettingsLib/res/values-as/arrays.xml index d0973f0aa8ae..b8353eebd5a4 100644 --- a/packages/SettingsLib/res/values-as/arrays.xml +++ b/packages/SettingsLib/res/values-as/arrays.xml @@ -138,9 +138,12 @@ <item msgid="4681409244565426925">"সংযোগৰ ক্ষমতা অনুযায়ী সৰ্বোত্তম"</item> <item msgid="364670732877872677">"উত্তম প্ৰচেষ্টা (অভিযোজিত বিট ৰেইট)"</item> </string-array> - <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) --> + <string-array name="bluetooth_audio_active_device_summaries"> + <item msgid="4862957058729193940"></item> + <item msgid="6481691720774549651">", সক্ৰিয়"</item> + <item msgid="8962366465966010158">", সক্ৰিয় (মিডিয়া)"</item> + <item msgid="4046665544396189228">", সক্ৰিয় (ফ\'ন)"</item> + </string-array> <string-array name="select_logd_size_titles"> <item msgid="8665206199209698501">"অফ কৰক"</item> <item msgid="1593289376502312923">"৬৪কে."</item> diff --git a/packages/SettingsLib/res/values-bn/arrays.xml b/packages/SettingsLib/res/values-bn/arrays.xml index e186bb4e8724..6340034c1ede 100644 --- a/packages/SettingsLib/res/values-bn/arrays.xml +++ b/packages/SettingsLib/res/values-bn/arrays.xml @@ -138,9 +138,12 @@ <item msgid="4681409244565426925">"সংযোগের গুণমানের জন্য অপটিমাইজ করা হয়েছে"</item> <item msgid="364670732877872677">"সেরা প্রচেষ্টা (অ্যাডাপ্টিভ বিট রেট)"</item> </string-array> - <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) --> + <string-array name="bluetooth_audio_active_device_summaries"> + <item msgid="4862957058729193940"></item> + <item msgid="6481691720774549651">", চালু আছে"</item> + <item msgid="8962366465966010158">", চালু আছে (মিডিয়া)"</item> + <item msgid="4046665544396189228">", চালু আছে (ফোন)"</item> + </string-array> <string-array name="select_logd_size_titles"> <item msgid="8665206199209698501">"বন্ধ আছে"</item> <item msgid="1593289376502312923">"৬৪K"</item> diff --git a/packages/SettingsLib/res/values-gu/arrays.xml b/packages/SettingsLib/res/values-gu/arrays.xml index 30a933973205..ecf5525ddb96 100644 --- a/packages/SettingsLib/res/values-gu/arrays.xml +++ b/packages/SettingsLib/res/values-gu/arrays.xml @@ -138,9 +138,12 @@ <item msgid="4681409244565426925">"કનેક્શનની ગુણવત્તા માટે ઓપ્ટિમાઇઝ કર્યું"</item> <item msgid="364670732877872677">"ઉત્તમ પ્રયાસ (અનુકૂલનશીલ બિટ રેટ)"</item> </string-array> - <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) --> + <string-array name="bluetooth_audio_active_device_summaries"> + <item msgid="4862957058729193940"></item> + <item msgid="6481691720774549651">", સક્રિય"</item> + <item msgid="8962366465966010158">", સક્રિય (મીડિયા)"</item> + <item msgid="4046665544396189228">", સક્રિય (ફોન)"</item> + </string-array> <string-array name="select_logd_size_titles"> <item msgid="8665206199209698501">"બંધ"</item> <item msgid="1593289376502312923">"64K"</item> diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml index 0e34b7726773..d1d01573188a 100644 --- a/packages/SettingsLib/res/values-hi/arrays.xml +++ b/packages/SettingsLib/res/values-hi/arrays.xml @@ -140,9 +140,9 @@ </string-array> <string-array name="bluetooth_audio_active_device_summaries"> <item msgid="4862957058729193940"></item> - <item msgid="6481691720774549651">"चालू है"</item> - <item msgid="8962366465966010158">"चालू है (सिर्फ़ मीडिया के लिए)"</item> - <item msgid="4046665544396189228">"चालू है (सिर्फ़ फ़ोन के लिए)"</item> + <item msgid="6481691720774549651">", चालू है"</item> + <item msgid="8962366465966010158">", चालू है (सिर्फ़ मीडिया के लिए)"</item> + <item msgid="4046665544396189228">", चालू है (सिर्फ़ फ़ोन के लिए)"</item> </string-array> <string-array name="select_logd_size_titles"> <item msgid="8665206199209698501">"बंद"</item> diff --git a/packages/SettingsLib/res/values-is/arrays.xml b/packages/SettingsLib/res/values-is/arrays.xml index 3aef4ce222f8..b0733711e5a3 100644 --- a/packages/SettingsLib/res/values-is/arrays.xml +++ b/packages/SettingsLib/res/values-is/arrays.xml @@ -138,9 +138,12 @@ <item msgid="4681409244565426925">"Fínstillt fyrir gæði tengingar"</item> <item msgid="364670732877872677">"Bestu mögulegu gæði (breytilegur bitahraði)"</item> </string-array> - <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) --> + <string-array name="bluetooth_audio_active_device_summaries"> + <item msgid="4862957058729193940"></item> + <item msgid="6481691720774549651">", virkt"</item> + <item msgid="8962366465966010158">", virkt (hljóð- og myndefni)"</item> + <item msgid="4046665544396189228">", virkt (sími)"</item> + </string-array> <string-array name="select_logd_size_titles"> <item msgid="8665206199209698501">"Slökkt"</item> <item msgid="1593289376502312923">"64 k"</item> diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml index 037c23f41c6c..8b6b8ca7fd29 100644 --- a/packages/SettingsLib/res/values-iw/arrays.xml +++ b/packages/SettingsLib/res/values-iw/arrays.xml @@ -138,9 +138,12 @@ <item msgid="4681409244565426925">"אופטימיזציה להשגת איכות חיבור מרבית"</item> <item msgid="364670732877872677">"האיכות הטובה ביותר (קצב העברת נתונים מותאם)"</item> </string-array> - <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) --> + <string-array name="bluetooth_audio_active_device_summaries"> + <item msgid="4862957058729193940"></item> + <item msgid="6481691720774549651">", פעיל"</item> + <item msgid="8962366465966010158">", פעיל (מדיה)"</item> + <item msgid="4046665544396189228">", פעיל (טלפון)"</item> + </string-array> <string-array name="select_logd_size_titles"> <item msgid="8665206199209698501">"כבוי"</item> <item msgid="1593289376502312923">"64K"</item> diff --git a/packages/SettingsLib/res/values-kn/arrays.xml b/packages/SettingsLib/res/values-kn/arrays.xml index a66af9585191..75d5dcb3c049 100644 --- a/packages/SettingsLib/res/values-kn/arrays.xml +++ b/packages/SettingsLib/res/values-kn/arrays.xml @@ -138,9 +138,12 @@ <item msgid="4681409244565426925">"ಸಂಪರ್ಕ ಗುಣಮಟ್ಟಕ್ಕಾಗಿ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗಿದೆ"</item> <item msgid="364670732877872677">"ಅತ್ಯುತ್ತಮ ಪ್ರಯತ್ನ (ಹೊಂದಿಸಬಹುದಾದ ಬಿಟ್ ಪ್ರಮಾಣ)"</item> </string-array> - <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) --> + <string-array name="bluetooth_audio_active_device_summaries"> + <item msgid="4862957058729193940"></item> + <item msgid="6481691720774549651">"ಸಕ್ರಿಯ"</item> + <item msgid="8962366465966010158">", ಸಕ್ರಿಯ (ಮಾಧ್ಯಮ)"</item> + <item msgid="4046665544396189228">", ಸಕ್ರಿಯ (ಫೋನ್)"</item> + </string-array> <string-array name="select_logd_size_titles"> <item msgid="8665206199209698501">"ಆಫ್"</item> <item msgid="1593289376502312923">"64K"</item> diff --git a/packages/SettingsLib/res/values-ml/arrays.xml b/packages/SettingsLib/res/values-ml/arrays.xml index 74689e38c3de..333791318d75 100644 --- a/packages/SettingsLib/res/values-ml/arrays.xml +++ b/packages/SettingsLib/res/values-ml/arrays.xml @@ -138,9 +138,12 @@ <item msgid="4681409244565426925">"കണക്ഷൻ നിലവാരമുയർത്താൻ ഒപ്റ്റിമൈസ് ചെയ്തു"</item> <item msgid="364670732877872677">"മികച്ച സംവിധാനം (അനുയോജ്യമായ ബിറ്റ് റേറ്റ്)"</item> </string-array> - <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) --> + <string-array name="bluetooth_audio_active_device_summaries"> + <item msgid="4862957058729193940"></item> + <item msgid="6481691720774549651">", സജീവം"</item> + <item msgid="8962366465966010158">", സജീവ (മീഡിയ)"</item> + <item msgid="4046665544396189228">", സജീവമായ (ഫോൺ)"</item> + </string-array> <string-array name="select_logd_size_titles"> <item msgid="8665206199209698501">"ഓഫ്"</item> <item msgid="1593289376502312923">"64K"</item> diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml index 841e56421c0a..f5bf2ea8666a 100644 --- a/packages/SettingsLib/res/values-mr/arrays.xml +++ b/packages/SettingsLib/res/values-mr/arrays.xml @@ -138,9 +138,12 @@ <item msgid="4681409244565426925">"कनेक्शन गुणवत्तेसाठी ऑप्टिमाइझ केले"</item> <item msgid="364670732877872677">"सर्वोत्तम प्रयत्न (अनुकूल बिट रेट)"</item> </string-array> - <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) --> + <string-array name="bluetooth_audio_active_device_summaries"> + <item msgid="4862957058729193940"></item> + <item msgid="6481691720774549651">", अॅक्टिव्ह"</item> + <item msgid="8962366465966010158">", अॅक्टिव्ह (मीडिया)"</item> + <item msgid="4046665544396189228">", अॅक्टिव्ह (फोन)"</item> + </string-array> <string-array name="select_logd_size_titles"> <item msgid="8665206199209698501">"बंद"</item> <item msgid="1593289376502312923">"64K"</item> diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml index 95a26d2245bc..4ed55cc9294e 100644 --- a/packages/SettingsLib/res/values-ne/arrays.xml +++ b/packages/SettingsLib/res/values-ne/arrays.xml @@ -138,9 +138,12 @@ <item msgid="4681409244565426925">"जडानको गुणस्तर सुधार्न अनुकूलन गरिएको"</item> <item msgid="364670732877872677">"उत्कृष्ट प्रयास (अनुकूलनीय बिट दर)"</item> </string-array> - <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) --> + <string-array name="bluetooth_audio_active_device_summaries"> + <item msgid="4862957058729193940"></item> + <item msgid="6481691720774549651">", सक्रिय"</item> + <item msgid="8962366465966010158">", सक्रिय (मिडिया)"</item> + <item msgid="4046665544396189228">", सक्रिय (फोन)"</item> + </string-array> <string-array name="select_logd_size_titles"> <item msgid="8665206199209698501">"निष्क्रिय गर्नुहोस्"</item> <item msgid="1593289376502312923">"६४के"</item> diff --git a/packages/SettingsLib/res/values-or/arrays.xml b/packages/SettingsLib/res/values-or/arrays.xml index 9da5d6e6a12a..18de8f9d649c 100644 --- a/packages/SettingsLib/res/values-or/arrays.xml +++ b/packages/SettingsLib/res/values-or/arrays.xml @@ -138,9 +138,12 @@ <item msgid="4681409244565426925">"ସଂଯୋଗର ଗୁଣବତ୍ତା ପାଇଁ ଅନୁକୂଳିତ"</item> <item msgid="364670732877872677">"ସର୍ବୋତ୍ତମ ପ୍ରୟାସ (ଅନୁକୂଳ ବିଟ୍ ରେଟ୍)"</item> </string-array> - <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) --> + <string-array name="bluetooth_audio_active_device_summaries"> + <item msgid="4862957058729193940"></item> + <item msgid="6481691720774549651">", ସକ୍ରିୟ"</item> + <item msgid="8962366465966010158">", ସକ୍ରିୟ (ମିଡିଆ)"</item> + <item msgid="4046665544396189228">", ସକ୍ରିୟ (ଫୋନ୍)"</item> + </string-array> <string-array name="select_logd_size_titles"> <item msgid="8665206199209698501">"ଅଫ୍"</item> <item msgid="1593289376502312923">"64K"</item> diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml index 2b3b91910107..ddb40ce96c94 100644 --- a/packages/SettingsLib/res/values-te/arrays.xml +++ b/packages/SettingsLib/res/values-te/arrays.xml @@ -138,9 +138,12 @@ <item msgid="4681409244565426925">"కనెక్షన్ నాణ్యత కోసం అనుకూలీకరించబడింది"</item> <item msgid="364670732877872677">"ఉత్తమ కృషి (అనుకూల బిట్ రేట్)"</item> </string-array> - <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) --> + <string-array name="bluetooth_audio_active_device_summaries"> + <item msgid="4862957058729193940"></item> + <item msgid="6481691720774549651">", సక్రియంగా ఉంది"</item> + <item msgid="8962366465966010158">", (మీడియా) సక్రియంగా ఉంది"</item> + <item msgid="4046665544396189228">", (ఫోన్) సక్రియంగా ఉంది"</item> + </string-array> <string-array name="select_logd_size_titles"> <item msgid="8665206199209698501">"ఆఫ్"</item> <item msgid="1593289376502312923">"64K"</item> diff --git a/packages/SettingsLib/res/values-ur/arrays.xml b/packages/SettingsLib/res/values-ur/arrays.xml index 6e2f90441366..c6eadd98b88a 100644 --- a/packages/SettingsLib/res/values-ur/arrays.xml +++ b/packages/SettingsLib/res/values-ur/arrays.xml @@ -138,9 +138,12 @@ <item msgid="4681409244565426925">"کنکشن کے معیار کیلئے بہتر بنایا گيا"</item> <item msgid="364670732877872677">"بہترین کوشش (اڈاپٹیو بٹ ریٹ)"</item> </string-array> - <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) --> - <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) --> + <string-array name="bluetooth_audio_active_device_summaries"> + <item msgid="4862957058729193940"></item> + <item msgid="6481691720774549651">"، فعال"</item> + <item msgid="8962366465966010158">"، فعال (میڈیا)"</item> + <item msgid="4046665544396189228">"، فعال (فون)"</item> + </string-array> <string-array name="select_logd_size_titles"> <item msgid="8665206199209698501">"آف"</item> <item msgid="1593289376502312923">"64K"</item> diff --git a/packages/Shell/res/values-hy/strings.xml b/packages/Shell/res/values-hy/strings.xml index cd57e766f6e3..95b6131c6884 100644 --- a/packages/Shell/res/values-hy/strings.xml +++ b/packages/Shell/res/values-hy/strings.xml @@ -28,7 +28,7 @@ <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Ընտրեք՝ վրիպակի զեկույցն առանց էկրանի պատկերի ուղարկելու համար կամ սպասեք էկրանի պատկերի ստեղծմանը"</string> <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Հպեք՝ վրիպակի զեկույցն առանց էկրանի պատկերի ուղարկելու համար կամ սպասեք էկրանի պատկերի ստեղծմանը"</string> <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Հպեք՝ վրիպակի զեկույցն առանց էկրանի պատկերի ուղարկելու համար կամ սպասեք էկրանի պատկերի ստեղծմանը"</string> - <string name="bugreport_confirm" msgid="5917407234515812495">"Վրիպակի զեկույցները պարունակում են տվյալներ համակարգի տարբեր մատյանի ֆայլերից և կարող են ներառել տեղեկություններ, որոնք դուք գաղտնի եք համարում (օրինակ՝ հավելվածի օգտագործման կամ տեղադրության մասին): Վրիպակի զեկույցները տրամադրեք միայն վստահելի մարդկանց և հավելվածներին:"</string> + <string name="bugreport_confirm" msgid="5917407234515812495">"Վրիպակի զեկույցները պարունակում են տվյալներ համակարգի տարբեր մատյաններից և կարող են ներառել տեղեկություններ, որոնք դուք գաղտնի եք համարում (օրինակ՝ հավելվածի օգտագործման կամ տեղադրության մասին): Վրիպակի զեկույցները տրամադրեք միայն վստահելի մարդկանց և հավելվածներին:"</string> <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Այլևս ցույց չտալ"</string> <string name="bugreport_storage_title" msgid="5332488144740527109">"Վրիպակների հաշվետվություններ"</string> <string name="bugreport_unreadable_text" msgid="586517851044535486">"Հնարավոր չէ կարդալ վրիպակների զեկույցի ֆայլը"</string> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml index db03ed20311c..dfa4bf922c8f 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml @@ -28,7 +28,7 @@ android:clipToPadding="false" android:orientation="vertical" android:layout_centerHorizontal="true"> - <com.android.systemui.statusbar.AlphaOptimizedTextView + <view class="com.android.keyguard.KeyguardSliceView$TitleView" android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res/drawable/ic_device_thermostat_24.xml b/packages/SystemUI/res/drawable/ic_device_thermostat_24.xml index 1972f6e4427c..90948dbc05a4 100644 --- a/packages/SystemUI/res/drawable/ic_device_thermostat_24.xml +++ b/packages/SystemUI/res/drawable/ic_device_thermostat_24.xml @@ -21,5 +21,5 @@ Copyright (C) 2016 The Android Open Source Project android:tint="?attr/colorControlNormal"> <path android:fillColor="#FFFFFFFF" - android:pathData="M15,13L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v8c-1.21,0.91 -2,2.37 -2,4 0,2.76 2.24,5 5,5s5,-2.24 5,-5c0,-1.63 -0.79,-3.09 -2,-4zM11,5c0,-0.55 0.45,-1 1,-1s1,0.45 1,1h-1v1h1v2h-1v1h1v2h-2L11,5z"/> + android:pathData="M18,9h-5v2h5M21,5h-8v2h8M12,18.97c0.62,-0.83 1,-1.85 1,-2.97c0,-1.63 -0.79,-3.09 -2,-4V6c0,-1.66 -1.34,-3 -3,-3C6.34,3 5,4.34 5,6v6c-1.21,0.91 -2,2.37 -2,4c0,1.12 0.38,2.14 1,2.97V19h0.02c0.91,1.21 2.35,2 3.98,2s3.06,-0.79 3.98,-2H12V18.97zM6.2,13.6L7,13v-1V6c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v6v1l0.8,0.6c0.75,0.57 1.2,1.46 1.2,2.4H5C5,15.06 5.45,14.16 6.2,13.6z"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_mic_26dp.xml b/packages/SystemUI/res/drawable/ic_mic_26dp.xml index 15c97b363509..1160c908799f 100644 --- a/packages/SystemUI/res/drawable/ic_mic_26dp.xml +++ b/packages/SystemUI/res/drawable/ic_mic_26dp.xml @@ -20,5 +20,8 @@ Copyright (C) 2017 The Android Open Source Project android:viewportHeight="24.0"> <path android:fillColor="#FF000000" - android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM18.08,11c-0.42,0 -0.77,0.3 -0.83,0.71 -0.37,2.61 -2.72,4.39 -5.25,4.39s-4.88,-1.77 -5.25,-4.39a0.839,0.839 0,0 0,-0.83 -0.71c-0.52,0 -0.92,0.46 -0.85,0.97 0.46,2.96 2.96,5.3 5.93,5.75v2.43c0,0.47 0.38,0.85 0.85,0.85h0.31c0.47,0 0.85,-0.38 0.85,-0.85v-2.43c2.96,-0.43 5.47,-2.78 5.93,-5.75a0.87,0.87 0,0 0,-0.86 -0.97z"/> + android:pathData="M12,14c1.66,0 3,-1.34 3,-3V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6C9,12.66 10.34,14 12,14zM11,5c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v6c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V5z"/> + <path + android:fillColor="#FF000000" + android:pathData="M17,11c0,2.76 -2.24,5 -5,5s-5,-2.24 -5,-5H5c0,3.53 2.61,6.43 6,6.92V21h2v-3.08c3.39,-0.49 6,-3.39 6,-6.92H17z"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_person.xml b/packages/SystemUI/res/drawable/ic_person.xml index 101911f1b022..1ee94019a2e0 100644 --- a/packages/SystemUI/res/drawable/ic_person.xml +++ b/packages/SystemUI/res/drawable/ic_person.xml @@ -16,9 +16,9 @@ Copyright (C) 2015 The Android Open Source Project <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24.0dp" android:height="24.0dp" - android:viewportWidth="48.0" - android:viewportHeight="48.0"> + android:viewportWidth="24.0" + android:viewportHeight="24.0"> <path - android:fillColor="#ffffffff" - android:pathData="M24 24c4.42 0 8,-3.59 8,-8 0,-4.42,-3.58,-8,-8,-8s-8 3.58,-8 8c0 4.41 3.58 8 8 8zm0 4c-5.33 0,-16 2.67,-16 8v4h32v-4c0,-5.33,-10.67,-8,-16,-8z"/> + android:fillColor="#FFFFFFFF" + android:pathData="M12,6c1.1,0 2,0.9 2,2s-0.9,2 -2,2s-2,-0.9 -2,-2S10.9,6 12,6M12,15c2.7,0 5.8,1.29 6,2v1H6l0,-0.99C6.2,16.29 9.3,15 12,15M12,4C9.79,4 8,5.79 8,8s1.79,4 4,4s4,-1.79 4,-4S14.21,4 12,4L12,4zM12,13c-2.67,0 -8,1.34 -8,4v3h16v-3C20,14.34 14.67,13 12,13L12,13z"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_branded_vpn.xml b/packages/SystemUI/res/drawable/ic_qs_branded_vpn.xml index 736a04a1da44..b20e1582698c 100644 --- a/packages/SystemUI/res/drawable/ic_qs_branded_vpn.xml +++ b/packages/SystemUI/res/drawable/ic_qs_branded_vpn.xml @@ -20,5 +20,8 @@ android:viewportHeight="24.0"> <path android:fillColor="#4DFFFFFF" - android:pathData="M12.700000,10.000000c-0.800000,-2.300000 -3.000000,-4.000000 -5.700000,-4.000000c-3.300000,0.000000 -6.000000,2.700000 -6.000000,6.000000s2.700000,6.000000 6.000000,6.000000c2.600000,0.000000 4.800000,-1.700000 5.700000,-4.000000L17.000000,14.000000l0.000000,4.000000l4.000000,0.000000l0.000000,-4.000000l2.000000,0.000000l0.000000,-4.000000L12.700000,10.000000zM7.000000,14.000000c-1.100000,0.000000 -2.000000,-0.900000 -2.000000,-2.000000c0.000000,-1.100000 0.900000,-2.000000 2.000000,-2.000000s2.000000,0.900000 2.000000,2.000000C9.000000,13.100000 8.100000,14.000000 7.000000,14.000000z"/> + android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h6v-3h2V9H12.09zM20,13h-2v3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H20V13z"/> + <path + android:fillColor="#4DFFFFFF" + android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_no_sim.xml b/packages/SystemUI/res/drawable/ic_qs_no_sim.xml index 5dcd9f78301b..22e12cc10405 100644 --- a/packages/SystemUI/res/drawable/ic_qs_no_sim.xml +++ b/packages/SystemUI/res/drawable/ic_qs_no_sim.xml @@ -16,14 +16,12 @@ Copyright (C) 2017 The Android Open Source Project <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="32dp" android:height="32dp" - android:viewportWidth="18.4" - android:viewportHeight="18.4"> - - <group - android:translateX="0.7" - android:translateY="1.0"> - <path - android:fillColor="#FFFFFFFF" - android:pathData="M13.91,11.84L5.14,3.08l1.81,-1.81h5.41c0.85,0 1.54,0.69 1.54,1.54l0.01,9.03zM15.06,14.95L2.54,2.44c-0.28,-0.28 -0.71,-0.28 -0.99,0s-0.28,0.71 0,0.98l1.53,1.53v8.67c0,0.85 0.69,1.54 1.54,1.54h7.74c0.27,0 0.52,-0.07 0.74,-0.2l0.96,0.96c0.28,0.28 0.71,0.28 0.99,0 0.28,-0.26 0.28,-0.69 0.01,-0.97z"/> - </group> + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M10.84,5H17v9.11l2,2V5c0,-1.1 -0.9,-2 -2,-2h-6.99L7.95,5.06l1.41,1.41L10.84,5z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M2.81,2.81L1.39,4.22L5,7.83V19c0,1.1 0.9,2 2,2h10c0.34,0 0.65,-0.1 0.93,-0.24l1.85,1.85l1.41,-1.41L2.81,2.81zM7,19V9.83L16.17,19H7z"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_vpn.xml b/packages/SystemUI/res/drawable/ic_qs_vpn.xml index cf24e1277d6d..6567d123f5d5 100644 --- a/packages/SystemUI/res/drawable/ic_qs_vpn.xml +++ b/packages/SystemUI/res/drawable/ic_qs_vpn.xml @@ -20,5 +20,8 @@ Copyright (C) 2014 The Android Open Source Project android:viewportHeight="24.0"> <path android:fillColor="#FFFFFFFF" - android:pathData="M12.700000,10.000000c-0.800000,-2.300000 -3.000000,-4.000000 -5.700000,-4.000000c-3.300000,0.000000 -6.000000,2.700000 -6.000000,6.000000s2.700000,6.000000 6.000000,6.000000c2.600000,0.000000 4.800000,-1.700000 5.700000,-4.000000L17.000000,14.000000l0.000000,4.000000l4.000000,0.000000l0.000000,-4.000000l2.000000,0.000000l0.000000,-4.000000L12.700000,10.000000zM7.000000,14.000000c-1.100000,0.000000 -2.000000,-0.900000 -2.000000,-2.000000c0.000000,-1.100000 0.900000,-2.000000 2.000000,-2.000000s2.000000,0.900000 2.000000,2.000000C9.000000,13.100000 8.100000,14.000000 7.000000,14.000000z"/> + android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h6v-3h2V9H12.09zM20,13h-2v3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H20V13z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_sim.xml b/packages/SystemUI/res/drawable/ic_sim.xml index 983c1d065472..a9a19027a8b1 100644 --- a/packages/SystemUI/res/drawable/ic_sim.xml +++ b/packages/SystemUI/res/drawable/ic_sim.xml @@ -19,9 +19,24 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> <path - android:pathData="M0 0h24v24H0z" - android:fillColor="#00000000"/> + android:fillColor="#FF000000" + android:pathData="M18,2h-8L4,8v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4C20,2.9 19.1,2 18,2zM18,4v16H6V8.83L10.83,4L18,4L18,4z"/> + <path + android:fillColor="#FF000000" + android:pathData="M7,17h2v2h-2z"/> + <path + android:fillColor="#FF000000" + android:pathData="M15,17h2v2h-2z"/> + <path + android:fillColor="#FF000000" + android:pathData="M7,11h2v4h-2z"/> + <path + android:fillColor="#FF000000" + android:pathData="M11,15h2v4h-2z"/> + <path + android:fillColor="#FF000000" + android:pathData="M11,11h2v2h-2z"/> <path android:fillColor="#FF000000" - android:pathData="M18.0,2.0l-8.0,0.0L4.02,8.0 4.0,20.0c0.0,1.0 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,4.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0z"/> + android:pathData="M15,11h2v4h-2z"/> </vector> diff --git a/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml b/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml index a86e5b9bdd26..bfae8575cd14 100644 --- a/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml +++ b/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml @@ -20,5 +20,8 @@ android:viewportHeight="24.0"> <path android:fillColor="#FFFFFFFF" - android:pathData="M12.700000,10.000000c-0.800000,-2.300000 -3.000000,-4.000000 -5.700000,-4.000000c-3.300000,0.000000 -6.000000,2.700000 -6.000000,6.000000s2.700000,6.000000 6.000000,6.000000c2.600000,0.000000 4.800000,-1.700000 5.700000,-4.000000L17.000000,14.000000l0.000000,4.000000l4.000000,0.000000l0.000000,-4.000000l2.000000,0.000000l0.000000,-4.000000L12.700000,10.000000zM7.000000,14.000000c-1.100000,0.000000 -2.000000,-0.900000 -2.000000,-2.000000c0.000000,-1.100000 0.900000,-2.000000 2.000000,-2.000000s2.000000,0.900000 2.000000,2.000000C9.000000,13.100000 8.100000,14.000000 7.000000,14.000000z"/> + android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h6v-3h2V9H12.09zM20,13h-2v3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H20V13z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/> </vector> diff --git a/packages/SystemUI/res/drawable/stat_sys_no_sims.xml b/packages/SystemUI/res/drawable/stat_sys_no_sims.xml index faea7d1bf949..5c9be5c50a13 100644 --- a/packages/SystemUI/res/drawable/stat_sys_no_sims.xml +++ b/packages/SystemUI/res/drawable/stat_sys_no_sims.xml @@ -19,13 +19,12 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="17dp" android:height="17dp" - android:viewportWidth="18.4" - android:viewportHeight="18.4"> - <group - android:translateX="0.7" - android:translateY="1.0"> - <path - android:pathData="M13.91,11.84L5.14,3.08l1.81,-1.81h5.41c0.85,0 1.54,0.69 1.54,1.54l0.01,9.03zM15.06,14.95L2.54,2.44c-0.28,-0.28 -0.71,-0.28 -0.99,0s-0.28,0.71 0,0.98l1.53,1.53v8.67c0,0.85 0.69,1.54 1.54,1.54h7.74c0.27,0 0.52,-0.07 0.74,-0.2l0.96,0.96c0.28,0.28 0.71,0.28 0.99,0 0.28,-0.26 0.28,-0.69 0.01,-0.97z" - android:fillColor="?attr/backgroundColor"/> - </group> + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h6v-3h2V9H12.09zM20,13h-2v3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H20V13z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/> </vector> diff --git a/packages/SystemUI/res/drawable/stat_sys_vpn_ic.xml b/packages/SystemUI/res/drawable/stat_sys_vpn_ic.xml index 66f4fbbea8ad..5c9be5c50a13 100644 --- a/packages/SystemUI/res/drawable/stat_sys_vpn_ic.xml +++ b/packages/SystemUI/res/drawable/stat_sys_vpn_ic.xml @@ -19,13 +19,12 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="17dp" android:height="17dp" - android:viewportWidth="19.0" - android:viewportHeight="19.0"> - <group - android:translateX="1.0" - android:translateY="1.0"> - <path - android:pathData="M15.46,6.96H9a4.637,4.637 0,0 0,-4.37 -3.09C2.07,3.87 0,5.94 0,8.5s2.07,4.63 4.63,4.63c2.02,0 3.73,-1.29 4.37,-3.09h2.2v1.54a1.54,1.54 0,0 0,3.08 0v-1.54h1.16c0.87,0 1.56,-0.69 1.56,-1.54s-0.69,-1.54 -1.54,-1.54zM4.63,10.04a1.54,1.54 0,1 1,0.001 -3.081,1.54 1.54,0 0,1 -0.001,3.081z" - android:fillColor="#FFF"/> - </group> + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h6v-3h2V9H12.09zM20,13h-2v3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H20V13z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/> </vector> diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml index e902c925c2a9..b5d48b4636a8 100644 --- a/packages/SystemUI/res/layout/remote_input.xml +++ b/packages/SystemUI/res/layout/remote_input.xml @@ -42,7 +42,7 @@ android:singleLine="true" android:ellipsize="start" android:inputType="textShortMessage|textAutoCorrect|textCapSentences" - android:imeOptions="actionSend" /> + android:imeOptions="actionSend|flagNoExtractUi|flagNoFullscreen" /> <FrameLayout android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index cf72d9fe5b3b..974c80e51357 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -436,8 +436,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Moenie weer wys nie"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Vee alles uit"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Bestuur kennisgewings"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Kennisgewings onderbreek deur Moenie Steur Nie"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Begin nou"</string> <string name="empty_shade_text" msgid="708135716272867002">"Geen kennisgewings nie"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"Profiel kan gemonitor word"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 44eebffde024..612db5558abc 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -436,8 +436,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"ዳግመኛ አታሳይ"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"ሁሉንም አጽዳ"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"ማሳወቂያዎችን ያስተዳድሩ"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"ማሳወቂያዎች በአትረብሽ ባሉበት ቆመዋል"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"አሁን ጀምር"</string> <string name="empty_shade_text" msgid="708135716272867002">"ምንም ማሳወቂያ የለም"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"መገለጫ ክትትል ሊደረግበት ይችላል"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 771626b18a68..c975de1cc2fe 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -436,8 +436,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Daha göstərmə"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Hamısını silin"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Bildirişləri idarə edin"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Bildirişlər \"Narahat Etməyin\" rejimi tərəfindən dayandırıldı"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"İndi başlayın"</string> <string name="empty_shade_text" msgid="708135716272867002">"Heç bir bildiriş yoxdur"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"Profil izlənə bilər"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index ee8051e91dba..33a95a6f6aa2 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -439,8 +439,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Ne prikazuj opet"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Očisti sve"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Upravljajte obavještenjima"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Značajka Ne uznemiravaj pauzirala je Obavijesti"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Započni odmah"</string> <string name="empty_shade_text" msgid="708135716272867002">"Nema obavještenja"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"Profil može biti nadziran"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 040412d218e2..766b241f72e9 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -436,8 +436,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Don\'t show again"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Clear all"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Manage notifications"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifications paused by Do not disturb"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Start now"</string> <string name="empty_shade_text" msgid="708135716272867002">"No notifications"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"Profile may be monitored"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 9b25c7ba17ec..306723ae11c7 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -436,8 +436,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Don\'t show again"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Clear all"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Manage notifications"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifications paused by Do not disturb"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Start now"</string> <string name="empty_shade_text" msgid="708135716272867002">"No notifications"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"Profile may be monitored"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 040412d218e2..766b241f72e9 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -436,8 +436,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Don\'t show again"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Clear all"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Manage notifications"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifications paused by Do not disturb"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Start now"</string> <string name="empty_shade_text" msgid="708135716272867002">"No notifications"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"Profile may be monitored"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 040412d218e2..766b241f72e9 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -436,8 +436,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Don\'t show again"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Clear all"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Manage notifications"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifications paused by Do not disturb"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Start now"</string> <string name="empty_shade_text" msgid="708135716272867002">"No notifications"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"Profile may be monitored"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 545e15aa548b..11ca904c5fa3 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -436,8 +436,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Don\'t show again"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Clear all"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Manage notifications"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifications paused by Do Not Disturb"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Start now"</string> <string name="empty_shade_text" msgid="708135716272867002">"No notifications"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"Profile may be monitored"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index d65d6e26ea4a..e0d330a321f9 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -436,8 +436,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"دوباره نشان داده نشود"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"پاک کردن همه موارد"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"مدیریت اعلانها"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"اعلانها توسط «مزاحم نشوید» موقتاً متوقف شدند"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"اکنون شروع شود"</string> <string name="empty_shade_text" msgid="708135716272867002">"اعلانی موجود نیست"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"شاید نمایه کنترل شود"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 9d47dff9706c..d1980b42fdee 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -438,8 +438,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Ne plus afficher"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Tout effacer"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Gérer les notifications"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Les notifications sont suspendues par le mode Ne pas déranger"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Commencer"</string> <string name="empty_shade_text" msgid="708135716272867002">"Aucune notification"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"le profil peut être contrôlé"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 8e131e45f865..61d72a5bc797 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -439,8 +439,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Ne prikazuj ponovo"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Izbriši sve"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Upravljanje obavijestima"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Značajka Ne uznemiravaj pauzirala je Obavijesti"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Započni sad"</string> <string name="empty_shade_text" msgid="708135716272867002">"Nema obavijesti"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"Profil se možda nadzire"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 415d8c9d1fb4..eb0cf08ee581 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -436,8 +436,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Jangan tampilkan lagi"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Hapus semua"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Kelola notifikasi"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifikasi dijeda oleh mode Jangan Ganggu"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Mulai sekarang"</string> <string name="empty_shade_text" msgid="708135716272867002">"Tidak ada notifikasi"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"Profil dapat dipantau"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 41b48606aeb5..26b607ed776b 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -438,8 +438,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Non mostrare più"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Cancella tutto"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Gestisci le notifiche"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifiche messe in pausa in base alla modalità Non disturbare"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Avvia adesso"</string> <string name="empty_shade_text" msgid="708135716272867002">"Nessuna notifica"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"Il profilo potrebbe essere monitorato"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 1c90e95af724..cc2613b696f1 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -438,8 +438,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"次回から表示しない"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"すべて消去"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"通知を管理する"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"マナーモードにより通知は一時停止中です"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"今すぐ開始"</string> <string name="empty_shade_text" msgid="708135716272867002">"通知はありません"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"プロファイルが監視されている可能性があります"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index a602c454c723..72a897dd988d 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -436,8 +436,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"နောက်ထပ် မပြပါနှင့်"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"အားလုံး ဖယ်ရှားရန်"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"အကြောင်းကြားချက်များကို စီမံရန်"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"အကြောင်းကြားချက်များကို \'မနှောင့်ယှက်ရ\' က ခေတ္တရပ်ထားသည်"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"ယခု စတင်ပါ"</string> <string name="empty_shade_text" msgid="708135716272867002">"အကြောင်းကြားချက်များ မရှိ"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"ပရိုဖိုင်ကို စောင့်ကြပ်နိုင်သည်"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 03bee6f8e8b9..a730586aade5 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -436,8 +436,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Niet opnieuw weergeven"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Alles wissen"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Meldingen beheren"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Meldingen onderbroken door \'Niet storen\'"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Nu starten"</string> <string name="empty_shade_text" msgid="708135716272867002">"Geen meldingen"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"Profiel kan worden gecontroleerd"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index f70eecd45bfc..074f8417ea64 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -438,8 +438,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Não mostrar novamente"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Limpar tudo"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Gerenciar notificações"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notificações pausadas pelo modo \"Não perturbe\""</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Iniciar agora"</string> <string name="empty_shade_text" msgid="708135716272867002">"Sem notificações"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"O perfil pode ser monitorado"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index aefd42a253c9..d505ac62a3b5 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -436,8 +436,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Não mostrar de novo"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Limpar tudo"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Gerir notificações"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notificações colocadas em pausa pelo modo Não incomodar."</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Começar agora"</string> <string name="empty_shade_text" msgid="708135716272867002">"Sem notificações"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"O perfil pode ser monitorizado"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index f70eecd45bfc..074f8417ea64 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -438,8 +438,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Não mostrar novamente"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Limpar tudo"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Gerenciar notificações"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notificações pausadas pelo modo \"Não perturbe\""</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Iniciar agora"</string> <string name="empty_shade_text" msgid="708135716272867002">"Sem notificações"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"O perfil pode ser monitorado"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index d3f5ac1f77f1..ebfaba643878 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -436,8 +436,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"නැවත නොපෙන්වන්න"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"සියල්ල හිස් කරන්න"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"දැනුම් දීම් කළමනාකරණය කරන්න"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"බාධා නොකරන්න මගින් විරාම කරන ලද දැනුම්දීම්"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"දැන් අරඹන්න"</string> <string name="empty_shade_text" msgid="708135716272867002">"දැනුම්දීම් නැත"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"ඇතැම් විට පැතිකඩ නිරීක්ෂණය කරන ලදි"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 95c0b9295921..38656addaf45 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -444,8 +444,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Nabudúce nezobrazovať"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Vymazať všetko"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Spravovať upozornenia"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Upozornenia sú pozastavené režimom Nerušiť"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Spustiť"</string> <string name="empty_shade_text" msgid="708135716272867002">"Žiadne upozornenia"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"Profil môže byť monitorovaný"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 9645253c54f7..5ccfc3700d64 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -444,8 +444,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Tega ne prikaži več"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Izbriši vse"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Upravljanje obvestil"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Prikazovanje obvestil je začasno zaustavljeno z načinom »ne moti«"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Začni zdaj"</string> <string name="empty_shade_text" msgid="708135716272867002">"Ni obvestil"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"Profil je morda nadziran"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 67ba61fed616..871a2501c3e3 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -436,8 +436,7 @@ <string name="media_projection_remember_text" msgid="3103510882172746752">"Ungabonisi futhi"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Sula konke"</string> <string name="manage_notifications_text" msgid="8035284146227267681">"Phatha izaziso"</string> - <!-- no translation found for dnd_suppressing_shade_text (1904574852846769301) --> - <skip /> + <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Izaziso zimiswe okwesikhashana ukungaphazamisi"</string> <string name="media_projection_action_text" msgid="8470872969457985954">"Qala manje"</string> <string name="empty_shade_text" msgid="708135716272867002">"Azikho izaziso"</string> <string name="profile_owned_footer" msgid="8021888108553696069">"Iphrofayela ingaqashwa"</string> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index c1458b2e5ce6..0c7c60010818 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -34,10 +34,10 @@ <dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen> <!-- Height of the battery icon in the status bar. --> - <dimen name="status_bar_battery_icon_height">14.5dp</dimen> + <dimen name="status_bar_battery_icon_height">13.0dp</dimen> <!-- Width of the battery icon in the status bar. --> - <dimen name="status_bar_battery_icon_width">9.5dp</dimen> + <dimen name="status_bar_battery_icon_width">8.5dp</dimen> <!-- The font size for the clock in the status bar. --> <dimen name="status_bar_clock_size">14sp</dimen> @@ -960,6 +960,10 @@ add about 88dp of height to the notifications. --> <dimen name="smart_reply_button_max_height">100dp</dimen> + <!-- The extra height that we allow a notification with a remote input history to be taller than + the regular notification, when we have remote input history texts present. --> + <dimen name="remote_input_history_extra_height">60dp</dimen> + <!-- Fingerprint Dialog values --> <dimen name="fingerprint_dialog_fp_icon_size">64dp</dimen> <dimen name="fingerprint_dialog_animation_translation_offset">350dp</dimen> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index f066e3401752..97cf1e51e1df 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -47,6 +47,7 @@ import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.keyguard.KeyguardSliceProvider; +import com.android.systemui.statusbar.AlphaOptimizedTextView; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.wakelock.KeepAwakeAnimationListener; @@ -196,6 +197,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe final SliceItem titleItem = rc.getTitleItem(); button.setText(titleItem == null ? null : titleItem.getText()); + button.setContentDescription(rc.getContentDescription()); Drawable iconDrawable = null; SliceItem icon = SliceQuery.find(item.getSlice(), @@ -244,7 +246,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe * @param charSequence Original text. * @return Optimal string. */ - private CharSequence findBestLineBreak(CharSequence charSequence) { + private static CharSequence findBestLineBreak(CharSequence charSequence) { if (TextUtils.isEmpty(charSequence)) { return charSequence; } @@ -370,27 +372,6 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe mIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.widget_icon_size); } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - // Find best ellipsis strategy for the title. - // Done on onMeasure since TextView#getLayout needs a measure pass to calculate its bounds. - Layout layout = mTitle.getLayout(); - if (layout != null) { - final int lineCount = layout.getLineCount(); - if (lineCount > 0) { - if (layout.getEllipsisCount(lineCount - 1) == 0) { - CharSequence title = mTitle.getText(); - CharSequence bestLineBreak = findBestLineBreak(title); - if (!TextUtils.equals(title, bestLineBreak)) { - mTitle.setText(bestLineBreak); - } - } - } - } - } - public void refresh() { Slice slice = SliceManager.getInstance(getContext()).bindSlice(mKeyguardSliceUri); onChanged(slice); @@ -553,6 +534,46 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe } } + /** + * A text view that will split its contents in 2 lines when possible. + */ + static class TitleView extends AlphaOptimizedTextView { + + public TitleView(Context context) { + super(context); + } + + public TitleView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public TitleView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public TitleView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + Layout layout = getLayout(); + int lineCount = layout.getLineCount(); + boolean ellipsizing = layout.getEllipsisCount(lineCount - 1) != 0; + if (lineCount > 0 && !ellipsizing) { + CharSequence title = getText(); + CharSequence bestLineBreak = findBestLineBreak(title); + if (!TextUtils.equals(title, bestLineBreak)) { + setText(bestLineBreak); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } + } + } + private class SliceViewTransitionListener implements LayoutTransition.TransitionListener { @Override public void startTransition(LayoutTransition transition, ViewGroup container, View view, diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index beb3c5398780..9c9f0218d74f 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -151,7 +151,6 @@ public class Dependency extends SystemUI { @Override public void start() { - sDependency = this; // TODO: Think about ways to push these creation rules out of Dependency to cut down // on imports. mProviders.put(TIME_TICK_HANDLER, () -> { @@ -331,6 +330,8 @@ public class Dependency extends SystemUI { // Put all dependencies above here so the factory can override them if it wants. SystemUIFactory.getInstance().injectDependencies(mProviders, mContext); + + sDependency = this; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index a7163bbaac04..d437555c62a4 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -94,6 +94,8 @@ public final class Prefs { String OVERVIEW_OPENED_FROM_HOME_COUNT = "OverviewOpenedFromHomeCount"; String HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDING = "HasSeenRecentsSwipeUpOnboarding"; String HAS_SEEN_RECENTS_QUICK_SCRUB_ONBOARDING = "HasSeenRecentsQuickScrubOnboarding"; + String DISMISSED_RECENTS_SWIPE_UP_ONBOARDING_COUNT = + "DismissedRecentsSwipeUpOnboardingCount"; String HAS_DISMISSED_RECENTS_QUICK_SCRUB_ONBOARDING_ONCE = "HasDismissedRecentsQuickScrubOnboardingOnce"; String SEEN_RINGER_GUIDANCE_COUNT = "RingerGuidanceCount"; diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 139215a02995..2746a71e3316 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -22,8 +22,8 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; -import static com.android.systemui.tuner.TunablePadding.FLAG_START; import static com.android.systemui.tuner.TunablePadding.FLAG_END; +import static com.android.systemui.tuner.TunablePadding.FLAG_START; import android.annotation.Dimension; import android.app.Fragment; @@ -66,6 +66,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.tuner.TunablePadding; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; +import com.android.systemui.util.leak.RotationUtils; /** * An overlay that draws screen decorations in software (e.g for rounded corners or display cutout) @@ -77,6 +78,9 @@ public class ScreenDecorations extends SystemUI implements Tunable { private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS = SystemProperties.getBoolean("debug.screenshot_rounded_corners", false); + private DisplayManager mDisplayManager; + private DisplayManager.DisplayListener mDisplayListener; + private int mRoundedDefault; private int mRoundedDefaultTop; private int mRoundedDefaultBottom; @@ -84,7 +88,7 @@ public class ScreenDecorations extends SystemUI implements Tunable { private View mBottomOverlay; private float mDensity; private WindowManager mWindowManager; - private boolean mLandscape; + private int mRotation; @Override public void start() { @@ -104,6 +108,28 @@ public class ScreenDecorations extends SystemUI implements Tunable { if (padding != 0) { setupPadding(padding); } + + mDisplayListener = new DisplayManager.DisplayListener() { + @Override + public void onDisplayAdded(int displayId) { + // do nothing + } + + @Override + public void onDisplayRemoved(int displayId) { + // do nothing + } + + @Override + public void onDisplayChanged(int displayId) { + updateOrientation(); + } + }; + + mRotation = -1; + mDisplayManager = (DisplayManager) mContext.getSystemService( + Context.DISPLAY_SERVICE); + mDisplayManager.registerDisplayListener(mDisplayListener, null); } private void setupDecorations() { @@ -169,17 +195,22 @@ public class ScreenDecorations extends SystemUI implements Tunable { @Override protected void onConfigurationChanged(Configuration newConfig) { - boolean newLanscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE; - if (newLanscape != mLandscape) { - mLandscape = newLanscape; + updateOrientation(); + } + + protected void updateOrientation() { + int newRotation = RotationUtils.getExactRotation(mContext); + if (newRotation != mRotation) { + mRotation = newRotation; if (mOverlay != null) { updateLayoutParams(); updateViews(); } - } - if (shouldDrawCutout() && mOverlay == null) { - setupDecorations(); + + if (shouldDrawCutout() && mOverlay == null) { + setupDecorations(); + } } } @@ -188,16 +219,28 @@ public class ScreenDecorations extends SystemUI implements Tunable { View topRight = mOverlay.findViewById(R.id.right); View bottomLeft = mBottomOverlay.findViewById(R.id.left); View bottomRight = mBottomOverlay.findViewById(R.id.right); - if (mLandscape) { - // Flip corners - View tmp = topRight; - topRight = bottomLeft; - bottomLeft = tmp; + + if (mRotation == RotationUtils.ROTATION_NONE) { + updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0); + updateView(topRight, Gravity.TOP | Gravity.RIGHT, 90); + updateView(bottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270); + updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180); + } else if (mRotation == RotationUtils.ROTATION_LANDSCAPE) { + updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0); + updateView(topRight, Gravity.BOTTOM | Gravity.LEFT, 270); + updateView(bottomLeft, Gravity.TOP | Gravity.RIGHT, 90);; + updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180); + } else if (mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) { + updateView(topLeft, Gravity.BOTTOM | Gravity.LEFT, 270); + updateView(topRight, Gravity.BOTTOM | Gravity.RIGHT, 180); + updateView(bottomLeft, Gravity.TOP | Gravity.LEFT, 0); + updateView(bottomRight, Gravity.TOP | Gravity.RIGHT, 90); + } else if (mRotation == RotationUtils.ROTATION_SEASCAPE) { + updateView(topLeft, Gravity.BOTTOM | Gravity.RIGHT, 180); + updateView(topRight, Gravity.TOP | Gravity.RIGHT, 90); + updateView(bottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270); + updateView(bottomRight, Gravity.TOP | Gravity.LEFT, 0); } - updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0); - updateView(topRight, Gravity.TOP | Gravity.RIGHT, 90); - updateView(bottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270); - updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180); updateWindowVisibilities(); } @@ -269,9 +312,14 @@ public class ScreenDecorations extends SystemUI implements Tunable { } lp.setTitle("ScreenDecorOverlay"); - lp.gravity = Gravity.TOP | Gravity.LEFT; + if (mRotation == RotationUtils.ROTATION_SEASCAPE + || mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) { + lp.gravity = Gravity.BOTTOM | Gravity.RIGHT; + } else { + lp.gravity = Gravity.TOP | Gravity.LEFT; + } lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - if (mLandscape) { + if (isLandscape(mRotation)) { lp.width = WRAP_CONTENT; lp.height = MATCH_PARENT; } @@ -281,7 +329,12 @@ public class ScreenDecorations extends SystemUI implements Tunable { private WindowManager.LayoutParams getBottomLayoutParams() { WindowManager.LayoutParams lp = getWindowLayoutParams(); lp.setTitle("ScreenDecorOverlayBottom"); - lp.gravity = Gravity.BOTTOM | Gravity.RIGHT; + if (mRotation == RotationUtils.ROTATION_SEASCAPE + || mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) { + lp.gravity = Gravity.TOP | Gravity.LEFT; + } else { + lp.gravity = Gravity.BOTTOM | Gravity.RIGHT; + } return lp; } @@ -568,4 +621,9 @@ public class ScreenDecorations extends SystemUI implements Tunable { return cutoutBounds; } } + + private boolean isLandscape(int rotation) { + return rotation == RotationUtils.ROTATION_LANDSCAPE || rotation == + RotationUtils.ROTATION_SEASCAPE; + } } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 721890f0aee9..03a639835377 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -56,6 +56,7 @@ import com.android.systemui.statusbar.phone.ScrimState; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; import com.android.systemui.statusbar.policy.SmartReplyConstants; import java.util.function.Consumer; @@ -148,5 +149,7 @@ public class SystemUIFactory { providers.put(NotificationEntryManager.class, () -> new NotificationEntryManager(context)); providers.put(KeyguardDismissUtil.class, KeyguardDismissUtil::new); providers.put(SmartReplyController.class, () -> new SmartReplyController()); + providers.put(RemoteInputQuickSettingsDisabler.class, + () -> new RemoteInputQuickSettingsDisabler(context)); } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index e9d78d9b4fdf..e2047bf89833 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -69,7 +69,7 @@ public class DozeFactory { new DozeScreenState(wrappedService, handler, params, wakeLock), createDozeScreenBrightness(context, wrappedService, sensorManager, host, params, handler), - new DozeWallpaperState(context, params) + new DozeWallpaperState(context) }); return machine; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java index 90140ff2d741..f14d396de383 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java @@ -42,7 +42,7 @@ public class DozeScreenState implements DozeMachine.Part { * hiding the wallpaper and changing the display mode is necessary to hide * the black frame that's inherent to hardware specs. */ - public static final int ENTER_DOZE_HIDE_WALLPAPER_DELAY = 2000; + public static final int ENTER_DOZE_HIDE_WALLPAPER_DELAY = 4500; private final DozeMachine.Service mDozeService; private final Handler mHandler; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java index 9d110fb74d86..47f86fed7e69 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java @@ -38,40 +38,33 @@ public class DozeWallpaperState implements DozeMachine.Part { private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final IWallpaperManager mWallpaperManagerService; - private boolean mKeyguardVisible; private boolean mIsAmbientMode; private final DozeParameters mDozeParameters; - public DozeWallpaperState(Context context, DozeParameters dozeParameters) { + public DozeWallpaperState(Context context) { this(IWallpaperManager.Stub.asInterface( ServiceManager.getService(Context.WALLPAPER_SERVICE)), - dozeParameters, KeyguardUpdateMonitor.getInstance(context)); + DozeParameters.getInstance(context)); } @VisibleForTesting - DozeWallpaperState(IWallpaperManager wallpaperManagerService, DozeParameters parameters, - KeyguardUpdateMonitor keyguardUpdateMonitor) { + DozeWallpaperState(IWallpaperManager wallpaperManagerService, DozeParameters parameters) { mWallpaperManagerService = wallpaperManagerService; mDozeParameters = parameters; - keyguardUpdateMonitor.registerCallback(new KeyguardUpdateMonitorCallback() { - @Override - public void onKeyguardVisibilityChanged(boolean showing) { - mKeyguardVisible = showing; - } - }); } @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { final boolean isAmbientMode; switch (newState) { + case DOZE: case DOZE_AOD: case DOZE_AOD_PAUSING: case DOZE_AOD_PAUSED: case DOZE_REQUEST_PULSE: case DOZE_PULSING: case DOZE_PULSE_DONE: - isAmbientMode = mDozeParameters.getAlwaysOn(); + isAmbientMode = true; break; default: isAmbientMode = false; @@ -81,7 +74,9 @@ public class DozeWallpaperState implements DozeMachine.Part { if (isAmbientMode) { animated = mDozeParameters.shouldControlScreenOff(); } else { - animated = !mDozeParameters.getDisplayNeedsBlanking(); + boolean wakingUpFromPulse = oldState == DozeMachine.State.DOZE_PULSING + && newState == DozeMachine.State.FINISH; + animated = !mDozeParameters.getDisplayNeedsBlanking() || wakingUpFromPulse; } if (isAmbientMode != mIsAmbientMode) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index c5e66f9f4f99..8b9bf776332b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -151,6 +151,8 @@ public class KeyguardSliceProvider extends SliceProvider implements return; } RowBuilder dndBuilder = new RowBuilder(builder, mDndUri) + .setContentDescription(getContext().getResources() + .getString(R.string.accessibility_quick_settings_dnd)) .addEndItem(Icon.createWithResource(getContext(), R.drawable.stat_sys_dnd)); builder.addRow(dndBuilder); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index cbd1ca1b771d..b146cfccf180 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -35,6 +35,7 @@ import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.FrameLayout.LayoutParams; +import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.R.id; @@ -43,6 +44,7 @@ import com.android.systemui.plugins.qs.QS; import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer; +import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; import com.android.systemui.statusbar.stack.StackStateAnimator; public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks { @@ -72,6 +74,9 @@ public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks { private float mLastQSExpansion = -1; private boolean mQsDisabled; + private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler = + Dependency.get(RemoteInputQuickSettingsDisabler.class); + @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { @@ -191,6 +196,8 @@ public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks { @Override public void disable(int state1, int state2, boolean animate) { + state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2); + final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0; if (disabled == mQsDisabled) return; mQsDisabled = disabled; diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 6fcb1c1152d1..7e4acc2d0e28 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -123,10 +123,16 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener */ class TaskStackListenerImpl extends SysUiTaskStackChangeListener { + private OverviewProxyService mOverviewProxyService; + + public TaskStackListenerImpl() { + mOverviewProxyService = Dependency.get(OverviewProxyService.class); + } + @Override public void onTaskStackChangedBackground() { // Skip background preloading recents in SystemUI if the overview services is bound - if (Dependency.get(OverviewProxyService.class).isEnabled()) { + if (mOverviewProxyService.isEnabled()) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java index ff0c11dfce52..bcc33d250e73 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static com.android.systemui.Prefs.Key.HAS_DISMISSED_RECENTS_QUICK_SCRUB_ONBOARDING_ONCE; +import static com.android.systemui.Prefs.Key.DISMISSED_RECENTS_SWIPE_UP_ONBOARDING_COUNT; import static com.android.systemui.Prefs.Key.HAS_SEEN_RECENTS_QUICK_SCRUB_ONBOARDING; import static com.android.systemui.Prefs.Key.HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDING; import static com.android.systemui.Prefs.Key.OVERVIEW_OPENED_COUNT; @@ -87,9 +88,17 @@ public class RecentsOnboarding { private static final int SWIPE_UP_SHOW_ON_OVERVIEW_OPENED_FROM_HOME_COUNT = 3; // Show quick scrub tips after opening overview this number of times. private static final int QUICK_SCRUB_SHOW_ON_OVERVIEW_OPENED_COUNT = 10; - // After explicitly dismissing, show again after launching this number of apps for swipe-up - // tips. + // Maximum number of dismissals while still showing swipe-up tips. + private static final int MAX_DISMISSAL_ON_SWIPE_UP_SHOW = 4; + // Number of dismissals for swipe-up tips when exponential backoff starts. + private static final int BACKOFF_DISMISSAL_COUNT_ON_SWIPE_UP_SHOW = 2; + // After explicitly dismissing for <= BACKOFF_DISMISSAL_COUNT_ON_SWIPE_UP_SHOW times, show again + // after launching this number of apps for swipe-up tips. private static final int SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS = 5; + // After explicitly dismissing for > BACKOFF_DISMISSAL_COUNT_ON_SWIPE_UP_SHOW but + // <= MAX_DISMISSAL_ON_SWIPE_UP_SHOW times, show again after launching this number of apps for + // swipe-up tips. + private static final int SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS_BACK_OFF = 10; private final Context mContext; private final WindowManager mWindowManager; @@ -152,9 +161,20 @@ public class RecentsOnboarding { if (getOpenedOverviewFromHomeCount() >= SWIPE_UP_SHOW_ON_OVERVIEW_OPENED_FROM_HOME_COUNT) { if (mHasDismissedSwipeUpTip) { + int hasDimissedSwipeUpOnboardingCount = + getDismissedSwipeUpOnboardingCount(); + if (hasDimissedSwipeUpOnboardingCount > MAX_DISMISSAL_ON_SWIPE_UP_SHOW) { + Log.d(TAG, "Should not be reached"); + return; + } + final int swipeUpShowOnAppLauncherAfterDismiss = + hasDimissedSwipeUpOnboardingCount + <= BACKOFF_DISMISSAL_COUNT_ON_SWIPE_UP_SHOW + ? SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS + : SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS_BACK_OFF; mNumAppsLaunchedSinceSwipeUpTipDismiss++; if (mNumAppsLaunchedSinceSwipeUpTipDismiss - == SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS) { + == swipeUpShowOnAppLauncherAfterDismiss) { mNumAppsLaunchedSinceSwipeUpTipDismiss = 0; shouldLog = show(R.string.recents_swipe_up_onboarding); } @@ -286,6 +306,10 @@ public class RecentsOnboarding { if (v.getTag().equals(R.string.recents_swipe_up_onboarding)) { mHasDismissedSwipeUpTip = true; mNumAppsLaunchedSinceSwipeUpTipDismiss = 0; + setDismissedSwipeUpOnboardingCount(getDismissedSwipeUpOnboardingCount() + 1); + if (getDismissedSwipeUpOnboardingCount() > MAX_DISMISSAL_ON_SWIPE_UP_SHOW) { + setHasSeenSwipeUpOnboarding(true); + } notifyOnTip(DISMISS, RECENTS_SWIPE_UP_ONBOARDING_TIP); } else { notifyOnTip(DISMISS, RECENTS_QUICK_SCRUB_ONBOARDING_TIP); @@ -304,6 +328,7 @@ public class RecentsOnboarding { if (RESET_PREFS_FOR_DEBUG) { setHasSeenSwipeUpOnboarding(false); setHasSeenQuickScrubOnboarding(false); + setDismissedSwipeUpOnboardingCount(0); setHasDismissedQuickScrubOnboardingOnce(false); setOpenedOverviewCount(0); setOpenedOverviewFromHomeCount(0); @@ -440,6 +465,8 @@ public class RecentsOnboarding { + mNumAppsLaunchedSinceSwipeUpTipDismiss); pw.println(" hasSeenSwipeUpOnboarding: " + hasSeenSwipeUpOnboarding()); pw.println(" hasSeenQuickScrubOnboarding: " + hasSeenQuickScrubOnboarding()); + pw.println(" getDismissedSwipeUpOnboardingCount: " + + getDismissedSwipeUpOnboardingCount()); pw.println(" hasDismissedQuickScrubOnboardingOnce: " + hasDismissedQuickScrubOnboardingOnce()); pw.println(" getOpenedOverviewCount: " + getOpenedOverviewCount()); @@ -488,6 +515,16 @@ public class RecentsOnboarding { } } + private int getDismissedSwipeUpOnboardingCount() { + return Prefs.getInt(mContext, DISMISSED_RECENTS_SWIPE_UP_ONBOARDING_COUNT, 0); + } + + private void setDismissedSwipeUpOnboardingCount(int dismissedSwipeUpOnboardingCount) { + Log.d(TAG, "setDismissedSwipeUpOnboardingCount: " + dismissedSwipeUpOnboardingCount); + Prefs.putInt(mContext, DISMISSED_RECENTS_SWIPE_UP_ONBOARDING_COUNT, + dismissedSwipeUpOnboardingCount); + } + private boolean hasDismissedQuickScrubOnboardingOnce() { return Prefs.getBoolean(mContext, HAS_DISMISSED_RECENTS_QUICK_SCRUB_ONBOARDING_ONCE, false); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java index 5e03fbfc1222..9f82bcf5bfe8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java @@ -19,8 +19,10 @@ package com.android.systemui.statusbar; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Point; import android.graphics.Rect; import android.util.AttributeSet; +import android.view.DisplayCutout; import android.view.View; import android.widget.TextView; @@ -44,8 +46,10 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { private boolean mPublicMode; private int mMaxWidth; private View mRootView; - private int mLeftInset; + private int mSysWinInset; + private int mCutOutInset; private Rect mIconDrawingRect = new Rect(); + private Point mPoint; private Runnable mOnDrawingRectChangedListener; public HeadsUpStatusBarView(Context context) { @@ -136,9 +140,20 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { int bottom = top + mIconPlaceholder.getHeight(); mLayoutedIconRect.set(left, top, right, bottom); updateDrawingRect(); - int targetPadding = mAbsoluteStartPadding + mLeftInset; + int targetPadding = mAbsoluteStartPadding + mSysWinInset + mCutOutInset; if (left != targetPadding) { - int newPadding = targetPadding - left + getPaddingStart(); + int start; + if (isLayoutRtl()) { + if (mPoint == null) { + mPoint = new Point(); + } + getDisplay().getRealSize(mPoint); + start = (mPoint.x - right); + } else { + start = left; + } + + int newPadding = targetPadding - start + getPaddingStart(); setPaddingRelative(newPadding, 0, mEndMargin, 0); } if (mFirstLayout) { @@ -150,9 +165,12 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { } } - @Override - public void setTranslationX(float translationX) { - super.setTranslationX(translationX); + public void setPanelTranslation(float translationX) { + if (isLayoutRtl()) { + setTranslationX(translationX + mCutOutInset); + } else { + setTranslationX(translationX - mCutOutInset); + } updateDrawingRect(); } @@ -167,7 +185,21 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { @Override protected boolean fitSystemWindows(Rect insets) { - mLeftInset = insets.left; + boolean isRtl = isLayoutRtl(); + mSysWinInset = isRtl ? insets.right : insets.left; + DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout(); + mCutOutInset = (displayCutout != null) + ? (isRtl ? displayCutout.getSafeInsetRight() : displayCutout.getSafeInsetLeft()) + : 0; + // For Double Cut Out mode, the System window navigation bar is at the right + // side of the left cut out. In this condition, mSysWinInset include the left cut + // out width so we set mCutOutInset to be 0. For RTL, the condition is the same. + // The navigation bar is at the left side of the right cut out and include the + // right cut out width. + if (mSysWinInset != 0) { + mCutOutInset = 0; + } + return super.fitSystemWindows(insets); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index 952c96111e64..70dad198a5a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -189,6 +189,7 @@ public class NotificationContentView extends FrameLayout { if (mExpandedSmartReplyView != null) { notificationMaxHeight += mExpandedSmartReplyView.getHeightUpperLimit(); } + notificationMaxHeight += mExpandedWrapper.getExtraMeasureHeight(); int size = notificationMaxHeight; ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams(); boolean useExactly = false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index 419e262bbc6f..e24d65aeab78 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -32,10 +32,13 @@ import android.app.AppGlobals; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; +import android.app.Person; import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.graphics.drawable.Icon; +import android.os.Bundle; +import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; import android.service.notification.NotificationListenerService.Ranking; @@ -50,6 +53,7 @@ import android.widget.RemoteViews; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.StatusBarIcon; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.NotificationColorUtil; import com.android.systemui.Dependency; import com.android.systemui.ForegroundServiceController; @@ -117,6 +121,11 @@ public class NotificationData { */ public Boolean mIsSystemNotification; + /** + * Has the user sent a reply through this Notification. + */ + private boolean hasSentReply; + public Entry(StatusBarNotification n) { this.key = n.getKey(); this.notification = n; @@ -298,6 +307,40 @@ public class NotificationData { lastRemoteInputSent = NOT_LAUNCHED_YET; remoteInputTextWhenReset = null; } + + public void setHasSentReply() { + hasSentReply = true; + } + + public boolean isLastMessageFromReply() { + if (!hasSentReply) { + return false; + } + Bundle extras = notification.getNotification().extras; + CharSequence[] replyTexts = extras.getCharSequenceArray( + Notification.EXTRA_REMOTE_INPUT_HISTORY); + if (!ArrayUtils.isEmpty(replyTexts)) { + return true; + } + Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES); + if (messages != null && messages.length > 0) { + Parcelable message = messages[messages.length - 1]; + if (message instanceof Bundle) { + Notification.MessagingStyle.Message lastMessage = + Notification.MessagingStyle.Message.getMessageFromBundle( + (Bundle) message); + if (lastMessage != null) { + Person senderPerson = lastMessage.getSenderPerson(); + if (senderPerson == null) { + return true; + } + Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON); + return Objects.equals(user, senderPerson); + } + } + } + return false; + } } private final ArrayMap<String, Entry> mEntries = new ArrayMap<>(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java index 2b7ab10a7b41..e52829a088e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java @@ -300,9 +300,11 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private void saveImportance() { if (!mIsNonblockable) { - // Only go through the lock screen/bouncer if the user didn't hit 'Keep showing'. + // Only go through the lock screen/bouncer if the user hit 'Stop notifications'. + // Otherwise, update the importance immediately. if (mCheckSaveListener != null - && !NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING.equals(mExitReason)) { + && NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS.equals( + mExitReason)) { mCheckSaveListener.checkSave(this::updateImportance, mSbn); } else { updateImportance(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java index 04c500fc8f6a..d6bef12a1384 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java @@ -88,6 +88,17 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, super(context, attrs, defStyleAttr, defStyleRes); } + @Override + public void getDrawingRect(Rect outRect) { + super.getDrawingRect(outRect); + float translationX = getTranslationX(); + float translationY = getTranslationY(); + outRect.left += translationX; + outRect.right += translationX; + outRect.top += translationY; + outRect.bottom += translationY; + } + private void init() { mMobileGroup = findViewById(R.id.mobile_group); mMobile = findViewById(R.id.mobile_signal); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java index ca00a5aed6d2..59a0adc75ff7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java @@ -151,6 +151,17 @@ public class StatusBarWifiView extends FrameLayout implements DarkReceiver, return mVisibleState; } + @Override + public void getDrawingRect(Rect outRect) { + super.getDrawingRect(outRect); + float translationX = getTranslationX(); + float translationY = getTranslationY(); + outRect.left += translationX; + outRect.right += translationX; + outRect.top += translationY; + outRect.bottom += translationY; + } + private void init() { int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme); int dualToneDarkTheme = Utils.getThemeAttr(mContext, R.attr.darkIconTheme); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java index 7a8d22bee2d8..dabe23eec558 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java @@ -28,4 +28,15 @@ public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManage CarStatusBar statusBar = (CarStatusBar) mStatusBar; statusBar.setNavBarVisibility(navBarVisible ? View.VISIBLE : View.GONE); } + + /** + * Car is a multi-user system. There's a cancel button on the bouncer that allows the user to + * go back to the user switcher and select another user. Different user may have different + * security mode which requires bouncer container to be resized. For this reason, the bouncer + * view is destroyed on cancel. + */ + @Override + protected boolean shouldDestroyViewOnReset() { + return true; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java index d040f7c1a8f5..ba265d877a8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java @@ -18,13 +18,13 @@ package com.android.systemui.statusbar.car; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.car.user.CarUserManagerHelper; import android.content.Context; import android.view.View; import android.view.ViewStub; import android.support.v7.widget.GridLayoutManager; +import com.android.settingslib.users.UserManagerHelper; import com.android.systemui.R; import com.android.systemui.statusbar.phone.StatusBar; @@ -37,7 +37,7 @@ public class FullscreenUserSwitcher { private final UserGridRecyclerView mUserGridView; private final int mShortAnimDuration; private final StatusBar mStatusBar; - private final CarUserManagerHelper mCarUserManagerHelper; + private final UserManagerHelper mUserManagerHelper; private int mCurrentForegroundUserId; private boolean mShowing; @@ -52,7 +52,7 @@ public class FullscreenUserSwitcher { mUserGridView.buildAdapter(); mUserGridView.setUserSelectionListener(this::onUserSelected); - mCarUserManagerHelper = new CarUserManagerHelper(context); + mUserManagerHelper = new UserManagerHelper(context); updateCurrentForegroundUser(); mShortAnimDuration = mContainer.getResources() @@ -84,11 +84,11 @@ public class FullscreenUserSwitcher { } private boolean foregroundUserChanged() { - return mCurrentForegroundUserId != mCarUserManagerHelper.getCurrentForegroundUserId(); + return mCurrentForegroundUserId != mUserManagerHelper.getForegroundUserId(); } private void updateCurrentForegroundUser() { - mCurrentForegroundUserId = mCarUserManagerHelper.getCurrentForegroundUserId(); + mCurrentForegroundUserId = mUserManagerHelper.getForegroundUserId(); } private void onUserSelected(UserGridRecyclerView.UserRecord record) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java index dda25ec77985..aaf1989d162e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java @@ -21,7 +21,6 @@ import static android.content.DialogInterface.BUTTON_POSITIVE; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.app.Dialog; -import android.car.user.CarUserManagerHelper; import android.content.Context; import android.content.DialogInterface; import android.content.pm.UserInfo; @@ -42,6 +41,7 @@ import android.widget.TextView; import androidx.car.widget.PagedListView; import com.android.internal.util.UserIcons; +import com.android.settingslib.users.UserManagerHelper; import com.android.systemui.R; import com.android.systemui.statusbar.phone.SystemUIDialog; @@ -53,16 +53,16 @@ import java.util.List; * One of the uses of this is for the lock screen in auto. */ public class UserGridRecyclerView extends PagedListView implements - CarUserManagerHelper.OnUsersUpdateListener { + UserManagerHelper.OnUsersUpdateListener { private UserSelectionListener mUserSelectionListener; private UserAdapter mAdapter; - private CarUserManagerHelper mCarUserManagerHelper; + private UserManagerHelper mUserManagerHelper; private Context mContext; public UserGridRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; - mCarUserManagerHelper = new CarUserManagerHelper(mContext); + mUserManagerHelper = new UserManagerHelper(mContext); } /** @@ -71,7 +71,7 @@ public class UserGridRecyclerView extends PagedListView implements @Override public void onFinishInflate() { super.onFinishInflate(); - mCarUserManagerHelper.registerOnUsersUpdateListener(this); + mUserManagerHelper.registerOnUsersUpdateListener(this); } /** @@ -80,7 +80,7 @@ public class UserGridRecyclerView extends PagedListView implements @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); - mCarUserManagerHelper.unregisterOnUsersUpdateListener(); + mUserManagerHelper.unregisterOnUsersUpdateListener(); } /** @@ -89,7 +89,7 @@ public class UserGridRecyclerView extends PagedListView implements * @return the adapter */ public void buildAdapter() { - List<UserRecord> userRecords = createUserRecords(mCarUserManagerHelper + List<UserRecord> userRecords = createUserRecords(mUserManagerHelper .getAllUsers()); mAdapter = new UserAdapter(mContext, userRecords); super.setAdapter(mAdapter); @@ -103,19 +103,19 @@ public class UserGridRecyclerView extends PagedListView implements continue; } boolean isForeground = - mCarUserManagerHelper.getCurrentForegroundUserId() == userInfo.id; + mUserManagerHelper.getForegroundUserId() == userInfo.id; UserRecord record = new UserRecord(userInfo, false /* isStartGuestSession */, false /* isAddUser */, isForeground); userRecords.add(record); } // Add guest user record if the foreground user is not a guest - if (!mCarUserManagerHelper.isForegroundUserGuest()) { + if (!mUserManagerHelper.foregroundUserIsGuestUser()) { userRecords.add(addGuestUserRecord()); } // Add add user record if the foreground user can add users - if (mCarUserManagerHelper.canForegroundUserAddUsers()) { + if (mUserManagerHelper.foregroundUserCanAddUsers()) { userRecords.add(addUserRecord()); } @@ -149,7 +149,7 @@ public class UserGridRecyclerView extends PagedListView implements @Override public void onUsersUpdate() { mAdapter.clearUsers(); - mAdapter.updateUsers(createUserRecords(mCarUserManagerHelper.getAllUsers())); + mAdapter.updateUsers(createUserRecords(mUserManagerHelper.getAllUsers())); mAdapter.notifyDataSetChanged(); } @@ -213,7 +213,7 @@ public class UserGridRecyclerView extends PagedListView implements // If the user selects Guest, start the guest session. if (userRecord.mIsStartGuestSession) { - mCarUserManagerHelper.startNewGuestSession(mGuestName); + mUserManagerHelper.startNewGuestSession(mGuestName); return; } @@ -240,14 +240,14 @@ public class UserGridRecyclerView extends PagedListView implements return; } // If the user doesn't want to be a guest or add a user, switch to the user selected - mCarUserManagerHelper.switchToUser(userRecord.mInfo); + mUserManagerHelper.switchToUser(userRecord.mInfo); }); } private Bitmap getUserRecordIcon(UserRecord userRecord) { if (userRecord.mIsStartGuestSession) { - return mCarUserManagerHelper.getGuestDefaultIcon(); + return mUserManagerHelper.getGuestDefaultIcon(); } if (userRecord.mIsAddUser) { @@ -255,7 +255,7 @@ public class UserGridRecyclerView extends PagedListView implements .getDrawable(R.drawable.car_add_circle_round)); } - return mCarUserManagerHelper.getUserIcon(userRecord.mInfo); + return mUserManagerHelper.getUserIcon(userRecord.mInfo); } @Override @@ -273,10 +273,7 @@ public class UserGridRecyclerView extends PagedListView implements @Override protected UserInfo doInBackground(String... userNames) { - // Default to create a non admin user for now. Need to add logic - // for user to choose whether they want to create an admin or non-admin - // user later. - return mCarUserManagerHelper.createNewNonAdminUser(userNames[0]); + return mUserManagerHelper.createNewUser(userNames[0]); } @Override @@ -286,7 +283,7 @@ public class UserGridRecyclerView extends PagedListView implements @Override protected void onPostExecute(UserInfo user) { if (user != null) { - mCarUserManagerHelper.switchToUser(user); + mUserManagerHelper.switchToUser(user); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java index 2d983c4d941b..03791c7f91ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java @@ -34,6 +34,7 @@ import android.widget.TextView; import com.android.internal.util.NotificationColorUtil; import com.android.internal.widget.NotificationActionListLayout; import com.android.systemui.Dependency; +import com.android.systemui.R; import com.android.systemui.UiOffloadThread; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.ExpandableNotificationRow; @@ -58,6 +59,7 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp private NotificationActionListLayout mActions; private ArraySet<PendingIntent> mCancelledPendingIntents = new ArraySet<>(); private UiOffloadThread mUiOffloadThread; + private View mRemoteInputHistory; protected NotificationTemplateViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { @@ -146,6 +148,8 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp mActionsContainer = mView.findViewById(com.android.internal.R.id.actions_container); mActions = mView.findViewById(com.android.internal.R.id.actions); mReplyAction = mView.findViewById(com.android.internal.R.id.reply_icon_action); + mRemoteInputHistory = mView.findViewById( + com.android.internal.R.id.notification_material_reply_container); updatePendingIntentCancellations(); } @@ -329,6 +333,10 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp if (mActions != null) { extra = mActions.getExtraMeasureHeight(); } + if (mRemoteInputHistory != null && mRemoteInputHistory.getVisibility() != View.GONE) { + extra += mRow.getContext().getResources().getDimensionPixelSize( + R.dimen.remote_input_history_extra_height); + } return extra + super.getExtraMeasureHeight(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 06f3c508585a..b52e3246f5db 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -16,8 +16,10 @@ package com.android.systemui.statusbar.phone; +import android.graphics.Point; import android.graphics.Rect; import android.view.View; +import android.view.WindowInsets; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; @@ -59,6 +61,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, private final View.OnLayoutChangeListener mStackScrollLayoutChangeListener = (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> updatePanelTranslation(); + Point mPoint; public HeadsUpAppearanceController( NotificationIconAreaController notificationIconAreaController, @@ -121,9 +124,55 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, updateHeader(headsUp.getEntry()); } + /** To count the distance from the window right boundary to scroller right boundary. The + * distance formula is the following: + * Y = screenSize - (SystemWindow's width + Scroller.getRight()) + * There are four modes MUST to be considered in Cut Out of RTL. + * No Cut Out: + * Scroller + NB + * NB + Scroller + * => SystemWindow = NavigationBar's width + * => Y = screenSize - (SystemWindow's width + Scroller.getRight()) + * Corner Cut Out or Tall Cut Out: + * cut out + Scroller + NB + * NB + Scroller + cut out + * => SystemWindow = NavigationBar's width + * => Y = screenSize - (SystemWindow's width + Scroller.getRight()) + * Double Cut Out: + * cut out left + Scroller + (NB + cut out right) + * SystemWindow = NavigationBar's width + cut out right width + * => Y = screenSize - (SystemWindow's width + Scroller.getRight()) + * (cut out left + NB) + Scroller + cut out right + * SystemWindow = NavigationBar's width + cut out left width + * => Y = screenSize - (SystemWindow's width + Scroller.getRight()) + * @return the translation X value for RTL. In theory, it should be negative. i.e. -Y + */ + private int getRtlTranslation() { + // TODO: Corner Cut Out still need to handle. + if (mPoint == null) { + mPoint = new Point(); + } + + int realDisplaySize = 0; + if (mStackScroller.getDisplay() != null) { + mStackScroller.getDisplay().getRealSize(mPoint); + realDisplaySize = mPoint.x; + } + + WindowInsets windowInset = mStackScroller.getRootWindowInsets(); + return windowInset.getSystemWindowInsetLeft() + mStackScroller.getRight() + + windowInset.getSystemWindowInsetRight() - realDisplaySize; + } + public void updatePanelTranslation() { - float newTranslation = mStackScroller.getLeft() + mStackScroller.getTranslationX(); - mHeadsUpStatusBarView.setTranslationX(newTranslation); + float newTranslation; + if (mStackScroller.isLayoutRtl()) { + newTranslation = getRtlTranslation(); + } else { + newTranslation = mStackScroller.getLeft(); + } + newTranslation += mStackScroller.getTranslationX(); + mHeadsUpStatusBarView.setPanelTranslation(newTranslation); } private void updateTopEntry() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index b043100d7b08..44d666eb0d65 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -141,7 +141,7 @@ public class NotificationIconAreaController implements DarkReceiver { } protected boolean shouldShowNotificationIcon(NotificationData.Entry entry, - boolean showAmbient, boolean hideDismissed) { + boolean showAmbient, boolean hideDismissed, boolean hideRepliedMessages) { if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) { return false; } @@ -155,6 +155,10 @@ public class NotificationIconAreaController implements DarkReceiver { return false; } + if (hideRepliedMessages && entry.isLastMessageFromReply()) { + return false; + } + // showAmbient == show in shade but not shelf if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(entry)) { return false; @@ -170,14 +174,15 @@ public class NotificationIconAreaController implements DarkReceiver { updateStatusBarIcons(); updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons, - NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */); + NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */, + false /* hideRepliedMessages */); applyNotificationIconsTint(); } - private void updateStatusBarIcons() { + public void updateStatusBarIcons() { updateIconsForLayout(entry -> entry.icon, mNotificationIcons, - false /* showAmbient */, true /* hideDismissed */); + false /* showAmbient */, true /* hideDismissed */, true /* hideRepliedMessages */); } /** @@ -187,9 +192,11 @@ public class NotificationIconAreaController implements DarkReceiver { * @param hostLayout which layout should be updated * @param showAmbient should ambient notification icons be shown * @param hideDismissed should dismissed icons be hidden + * @param hideRepliedMessages should messages that have been replied to be hidden */ private void updateIconsForLayout(Function<NotificationData.Entry, StatusBarIconView> function, - NotificationIconContainer hostLayout, boolean showAmbient, boolean hideDismissed) { + NotificationIconContainer hostLayout, boolean showAmbient, boolean hideDismissed, + boolean hideRepliedMessages) { ArrayList<StatusBarIconView> toShow = new ArrayList<>( mNotificationScrollLayout.getChildCount()); @@ -198,7 +205,8 @@ public class NotificationIconAreaController implements DarkReceiver { View view = mNotificationScrollLayout.getChildAt(i); if (view instanceof ExpandableNotificationRow) { NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry(); - if (shouldShowNotificationIcon(ent, showAmbient, hideDismissed)) { + if (shouldShowNotificationIcon(ent, showAmbient, hideDismissed, + hideRepliedMessages)) { toShow.add(function.apply(ent)); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 8c257fe255a3..5dee2a6000c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -129,13 +129,14 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { public static final int MAX_VISIBLE_ICONS_WHEN_DARK = 5; public static final int MAX_STATIC_ICONS = 4; - private static final int MAX_DOTS = 3; + private static final int MAX_DOTS = 1; private boolean mIsStaticLayout = true; private final HashMap<View, IconState> mIconStates = new HashMap<>(); private int mDotPadding; private int mStaticDotRadius; private int mStaticDotDiameter; + private int mOverflowWidth; private int mActualLayoutWidth = NO_VALUE; private float mActualPaddingEnd = NO_VALUE; private float mActualPaddingStart = NO_VALUE; @@ -230,7 +231,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { int top = (int) (centerY - height / 2.0f); child.layout(0, top, width, top + height); if (i == 0) { - mIconSize = child.getWidth(); + setIconSize(child.getWidth()); } } getLocationOnScreen(mAbsolutePosition); @@ -239,6 +240,11 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } } + private void setIconSize(int size) { + mIconSize = size; + mOverflowWidth = mIconSize + (MAX_DOTS - 1) * (mStaticDotDiameter + mDotPadding); + } + private void updateState() { resetViewStates(); calculateIconTranslations(); @@ -391,12 +397,12 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { iconState.visibleState = StatusBarIconView.STATE_ICON; boolean isOverflowing = - (translationX >= (noOverflowAfter ? layoutEnd - mIconSize : overflowStart)); + (translationX > (noOverflowAfter ? layoutEnd - mIconSize + : overflowStart - mIconSize)); if (firstOverflowIndex == -1 && (forceOverflow || isOverflowing)) { firstOverflowIndex = noOverflowAfter && !forceOverflow ? i - 1 : i; - mVisualOverflowStart = layoutEnd - mIconSize - - 2 * (mStaticDotDiameter + mDotPadding); - if (forceOverflow) { + mVisualOverflowStart = layoutEnd - mOverflowWidth; + if (forceOverflow || mIsStaticLayout) { mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart); } } @@ -408,7 +414,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { for (int i = firstOverflowIndex; i < childCount; i++) { View view = getChildAt(i); IconState iconState = mIconStates.get(view); - int dotWidth = mStaticDotRadius * 2 + mDotPadding; + int dotWidth = mStaticDotDiameter + mDotPadding; iconState.xTranslation = translationX; if (mNumDots < MAX_DOTS) { if (mNumDots == 0 && iconState.iconAppearAmount < 0.8f) { @@ -536,7 +542,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } private float getMaxOverflowStart() { - return getLayoutEnd() - mIconSize * (2 + OVERFLOW_EARLY_AMOUNT); + return getLayoutEnd() - mOverflowWidth; } public void setChangingViewPositions(boolean changingViewPositions) { @@ -607,7 +613,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { return 0; } - int collapsedPadding = mIconSize + 2 * (mStaticDotDiameter + mDotPadding); + int collapsedPadding = mOverflowWidth; if (collapsedPadding + getFinalTranslationX() > getWidth()) { collapsedPadding = getWidth() - getFinalTranslationX(); 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 b0d72c5d7ce7..c70f03405db3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -234,6 +234,7 @@ import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.policy.PreviewInflater; +import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; @@ -399,6 +400,9 @@ public class StatusBar extends SystemUI implements DemoMode, private View mPendingRemoteInputView; private View mPendingWorkRemoteInputView; + private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler = + Dependency.get(RemoteInputQuickSettingsDisabler.class); + private View mReportRejectedTouch; private int mMaxAllowedKeyguardNotifications; @@ -1759,6 +1763,8 @@ public class StatusBar extends SystemUI implements DemoMode, */ @Override public void disable(int state1, int state2, boolean animate) { + state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2); + animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN; final int old1 = mDisabled1; final int diff1 = state1 ^ old1; @@ -2090,7 +2096,8 @@ public class StatusBar extends SystemUI implements DemoMode, * @param animate should the change of the icons be animated. */ private void updateHideIconsForBouncer(boolean animate) { - boolean hideBecauseApp = mTopHidesStatusBar && mIsOccluded && mStatusBarWindowHidden; + boolean hideBecauseApp = mTopHidesStatusBar && mIsOccluded + && (mStatusBarWindowHidden || mBouncerShowing); boolean hideBecauseKeyguard = !mPanelExpanded && !mIsOccluded && mBouncerShowing; boolean shouldHideIconsForBouncer = hideBecauseApp || hideBecauseKeyguard; if (mHideIconsForBouncer != shouldHideIconsForBouncer) { @@ -4715,7 +4722,7 @@ public class StatusBar extends SystemUI implements DemoMode, // Bouncer needs the front scrim when it's on top of an activity, // tapping on a notification, editing QS or being dismissed by // FLAG_DISMISS_KEYGUARD_ACTIVITY. - ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming() + ScrimState state = mIsOccluded || mStatusBarKeyguardViewManager.bouncerNeedsScrimming() || mStatusBarKeyguardViewManager.willDismissWithAction() || mStatusBarKeyguardViewManager.isFullscreenBouncer() ? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER; 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 6a6a7dd08120..5f5d12e73f66 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -169,7 +169,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // • Full-screen user switcher is displayed. if (mNotificationPanelView.isUnlockHintRunning()) { mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); - } else if (mBouncer.willDismissWithAction() || mBouncer.isShowingScrimmed() + } else if (mOccluded || mBouncer.willDismissWithAction() || mBouncer.isShowingScrimmed() || mStatusBar.isFullScreenUserSwitcherState()) { mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); } else if (mShowing && !mDozing) { @@ -206,13 +206,17 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } else { mStatusBar.showKeyguard(); if (hideBouncerWhenShowing) { - hideBouncer(false /* destroyView */); + hideBouncer(shouldDestroyViewOnReset() /* destroyView */); mBouncer.prepare(); } } updateStates(); } + protected boolean shouldDestroyViewOnReset() { + return false; + } + private void hideBouncer(boolean destroyView) { mBouncer.hide(destroyView); cancelPendingWakeupAction(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java new file mode 100644 index 000000000000..c2933e1516be --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java @@ -0,0 +1,82 @@ +/* + * 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.systemui.statusbar.policy; + +import android.app.StatusBarManager; +import android.content.Context; +import android.content.res.Configuration; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.Dependency; +import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.qs.QSFragment; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.phone.StatusBar; + +/** + * Let {@link RemoteInputView} to control the visibility of QuickSetting. + */ +public class RemoteInputQuickSettingsDisabler + implements ConfigurationController.ConfigurationListener { + + private Context mContext; + @VisibleForTesting boolean mRemoteInputActive; + @VisibleForTesting boolean misLandscape; + private int mLastOrientation; + @VisibleForTesting CommandQueue mCommandQueue; + + public RemoteInputQuickSettingsDisabler(Context context) { + mContext = context; + mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class); + mLastOrientation = mContext.getResources().getConfiguration().orientation; + Dependency.get(ConfigurationController.class).addCallback(this); + } + + public int adjustDisableFlags(int state) { + if (mRemoteInputActive && misLandscape) { + state |= StatusBarManager.DISABLE2_QUICK_SETTINGS; + } + + return state; + } + + public void setRemoteInputActive(boolean active){ + if(mRemoteInputActive != active){ + mRemoteInputActive = active; + recomputeDisableFlags(); + } + } + + @Override + public void onConfigChanged(Configuration newConfig) { + if (newConfig.orientation != mLastOrientation) { + misLandscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE; + mLastOrientation = newConfig.orientation; + recomputeDisableFlags(); + } + } + + /** + * Reapplies the disable flags. Then the method adjustDisableFlags in this class will be invoked + * in {@link QSFragment#disable(int, int, boolean)} and + * {@link StatusBar#disable(int, int, boolean)} + * to modify the disable flags according to the status of mRemoteInputActive and misLandscape. + */ + private void recomputeDisableFlags() { + mCommandQueue.recomputeDisableFlags(true); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 310f14c2fca7..4ec9ae883a2b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -53,6 +53,7 @@ import android.widget.TextView; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; +import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.NotificationData; @@ -81,6 +82,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private RemoteInput[] mRemoteInputs; private RemoteInput mRemoteInput; private RemoteInputController mController; + private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; private NotificationData.Entry mEntry; @@ -96,6 +98,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene public RemoteInputView(Context context, AttributeSet attrs) { super(context, attrs); + mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class); } @Override @@ -151,6 +154,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mController.removeRemoteInput(mEntry, mToken); mEditText.mShowImeOnInputConnection = false; mController.remoteInputSent(mEntry); + mEntry.setHasSentReply(); // Tell ShortcutManager that this package has been "activated". ShortcutManager // will reset the throttling for this package. @@ -232,6 +236,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } } } + + mRemoteInputQuickSettingsDisabler.setRemoteInputActive(false); + MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE, mEntry.notification.getPackageName()); } @@ -291,6 +298,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mEditText.setSelection(mEditText.getText().length()); mEditText.requestFocus(); mController.addRemoteInput(mEntry, mToken); + + mRemoteInputQuickSettingsDisabler.setRemoteInputActive(true); + updateSendButton(); } @@ -554,6 +564,14 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } @Override + public boolean onKeyPreIme(int keyCode, KeyEvent event) { + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { + mRemoteInputView.mRemoteInputQuickSettingsDisabler.setRemoteInputActive(false); + } + return super.dispatchKeyEvent(event); + } + + @Override public boolean onCheckIsTextEditor() { // Stop being editable while we're being removed. During removal, we get reattached, // and editable views get their spellchecking state re-evaluated which is too costly @@ -565,11 +583,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { final InputConnection inputConnection = super.onCreateInputConnection(outAttrs); - //if pinned, set imeOption to keep the behavior like in portrait. - if (mRemoteInputView != null && mRemoteInputView.mEntry.row.isPinned()) { - outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI - | EditorInfo.IME_FLAG_NO_FULLSCREEN; - } if (mShowImeOnInputConnection && inputConnection != null) { final InputMethodManager imm = InputMethodManager.getInstance(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index 585787ebce60..924aa0120c94 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -213,6 +213,7 @@ public class SmartReplyView extends ViewGroup { Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND); RemoteInput.addResultsToIntent(new RemoteInput[]{remoteInput}, intent, results); RemoteInput.setResultsSource(intent, RemoteInput.SOURCE_CHOICE); + entry.setHasSentReply(); try { pendingIntent.send(context, 0, intent); } catch (PendingIntent.CanceledException e) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java index 91a4b07c2109..db3f0d997e08 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java @@ -30,7 +30,6 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.policy.HeadsUpManager; import java.util.ArrayList; -import java.util.Collection; /** * A global state to track all input states for the algorithm. @@ -72,6 +71,7 @@ public class AmbientState { private ExpandableNotificationRow mExpandingNotification; private int mDarkTopPadding; private float mDarkAmount; + private boolean mAppearing; public AmbientState(Context context) { reload(context); @@ -436,4 +436,12 @@ public class AmbientState { public int getDarkTopPadding() { return mDarkTopPadding; } + + public void setAppearing(boolean appearing) { + mAppearing = appearing; + } + + public boolean isAppearing() { + return mAppearing; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index bd56d7941a29..a2b33fa1a581 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -885,7 +885,9 @@ public class NotificationStackScrollLayout extends ViewGroup float appearEndPosition = getAppearEndPosition(); float appearStartPosition = getAppearStartPosition(); float appearFraction = 1.0f; - if (height >= appearEndPosition) { + boolean appearing = height < appearEndPosition; + mAmbientState.setAppearing(appearing); + if (!appearing) { translationY = 0; if (mShouldShowShelfOnly) { stackHeight = mTopPadding + mShelf.getIntrinsicHeight(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index 85f33d75e60c..0d50f5a9eef7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -478,22 +478,6 @@ public class StackScrollAlgorithm { childState.hidden = false; } } - // Let's hide all the views if we are not expanded. the views might otherwise be visible - // in the headsup area if a view was swiped away - if (!mIsExpanded) { - for (int i = 0; i < childCount; i++) { - boolean visible = false; - View child = algorithmState.visibleChildren.get(i); - if (child instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) child; - visible = row.isHeadsUp() || row.isHeadsUpAnimatingAway(); - } - if (!visible) { - ExpandableViewState childState = resultState.getViewStateForView(child); - childState.hidden = true; - } - } - } } private void clampHunToTop(AmbientState ambientState, ExpandableNotificationRow row, @@ -536,6 +520,10 @@ public class StackScrollAlgorithm { int shelfStart = ambientState.getInnerHeight() - ambientState.getShelf().getIntrinsicHeight(); + if (ambientState.isAppearing() && !child.isAboveShelf()) { + // Don't show none heads-up notifications while in appearing phase. + childViewState.yTranslation = Math.max(childViewState.yTranslation, shelfStart); + } childViewState.yTranslation = Math.min(childViewState.yTranslation, shelfStart); if (childViewState.yTranslation >= shelfStart) { childViewState.hidden = !child.isExpandAnimationRunning() && !child.hasExpandingChild(); diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java index d52746599aa1..2f8dfdc2991a 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java +++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java @@ -640,7 +640,7 @@ public class StorageNotification extends SystemUI { } private PendingIntent buildBrowsePendingIntent(VolumeInfo vol) { - final Intent intent = vol.buildBrowseIntent(); + final Intent intent = vol.buildBrowseIntentForUser(vol.getMountUserId()); final int requestKey = vol.getId().hashCode(); return PendingIntent.getActivityAsUser(mContext, requestKey, intent, diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java index ad2090009714..11c72c4c0cb9 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java @@ -23,6 +23,7 @@ public class RotationUtils { public static final int ROTATION_NONE = 0; public static final int ROTATION_LANDSCAPE = 1; public static final int ROTATION_SEASCAPE = 2; + public static final int ROTATION_UPSIDE_DOWN = 3; public static int getRotation(Context context) { Configuration config = context.getResources().getConfiguration(); @@ -36,4 +37,19 @@ public class RotationUtils { } return ROTATION_NONE; } + + public static int getExactRotation(Context context) { + Configuration config = context.getResources().getConfiguration(); + int rot = context.getDisplay().getRotation(); + if (config.smallestScreenWidthDp < 600) { + if (rot == Surface.ROTATION_90) { + return ROTATION_LANDSCAPE; + } else if (rot == Surface.ROTATION_270) { + return ROTATION_SEASCAPE; + } else if (rot == Surface.ROTATION_180) { + return ROTATION_UPSIDE_DOWN; + } + } + return ROTATION_NONE; + } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index d76b7f091870..03474a8c3385 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -727,6 +727,12 @@ public class VolumeDialogImpl implements VolumeDialog { } protected void onStateChangedH(State state) { + if (mState != null && state != null + && mState.ringerModeInternal != state.ringerModeInternal + && state.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) { + mController.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK)); + } + mState = state; mDynamic.clear(); // add any new dynamic rows diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java index 2f05b06ae0b0..f1bf31d7a58a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java @@ -36,6 +36,7 @@ import android.app.Fragment; import android.content.res.Configuration; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; import android.view.Display; import android.view.View; import android.view.WindowManager; @@ -54,6 +55,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +@RunWithLooper @RunWith(AndroidTestingRunner.class) @SmallTest public class ScreenDecorationsTest extends SysuiTestCase { @@ -98,6 +100,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { mContext.getOrCreateTestableResources().addOverride( com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false); mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 0); + mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius_top, 0); + mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius_bottom, 0); mContext.getOrCreateTestableResources() .addOverride(dimen.rounded_corner_content_padding, 0); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java index 5c80bb5ded9d..6ac44628b52f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java @@ -45,13 +45,11 @@ public class DozeWallpaperStateTest extends SysuiTestCase { private DozeWallpaperState mDozeWallpaperState; @Mock IWallpaperManager mIWallpaperManager; @Mock DozeParameters mDozeParameters; - @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mDozeWallpaperState = new DozeWallpaperState(mIWallpaperManager, mDozeParameters, - mKeyguardUpdateMonitor); + mDozeWallpaperState = new DozeWallpaperState(mIWallpaperManager, mDozeParameters); } @Test @@ -100,4 +98,28 @@ public class DozeWallpaperStateTest extends SysuiTestCase { mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH); verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(false)); } + + @Test + public void testTransitionTo_requestPulseIsAmbientMode() throws RemoteException { + mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE, + DozeMachine.State.DOZE_REQUEST_PULSE); + verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false)); + } + + @Test + public void testTransitionTo_pulseIsAmbientMode() throws RemoteException { + mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE, + DozeMachine.State.DOZE_PULSING); + verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false)); + } + + @Test + public void testTransitionTo_animatesWhenWakingUpFromPulse() throws RemoteException { + mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE, + DozeMachine.State.DOZE_PULSING); + reset(mIWallpaperManager); + mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_PULSING, + DozeMachine.State.FINISH); + verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(true)); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java index bdeb8bcd6fb8..a72fed4a2b41 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java @@ -508,7 +508,6 @@ public class NotificationInfoTest extends SysuiTestCase { anyString(), eq(TEST_UID), eq(true)); } - @Test public void testCloseControls_nonNullCheckSaveListenerDoesntDelayKeepShowing() throws Exception { @@ -537,6 +536,43 @@ public class NotificationInfoTest extends SysuiTestCase { anyString(), eq(TEST_UID), eq(true)); } + @Test + public void testCloseControls_nonNullCheckSaveListenerDoesntDelayDismiss() + throws Exception { + NotificationInfo.CheckSaveListener listener = + mock(NotificationInfo.CheckSaveListener.class); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, + 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */, + null /* onSettingsClick */, null /* onAppSettingsClick */ , + false /* isNonblockable */, true /* isForBlockingHelper */, + true /* isUserSentimentNegative */); + + mNotificationInfo.handleCloseControls(true /* save */, false /* force */); + + mTestableLooper.processAllMessages(); + verify(listener, times(0)).checkSave(any(Runnable.class), eq(mSbn)); + } + + @Test + public void testCloseControls_checkSaveListenerDelaysStopNotifications() + throws Exception { + NotificationInfo.CheckSaveListener listener = + mock(NotificationInfo.CheckSaveListener.class); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, + 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */, + null /* onSettingsClick */, null /* onAppSettingsClick */ , + false /* isNonblockable */, true /* isForBlockingHelper */, + true /* isUserSentimentNegative */); + + mNotificationInfo.findViewById(R.id.block).performClick(); + waitForUndoButton(); + mNotificationInfo.handleCloseControls(true /* save */, false /* force */); + + mTestableLooper.processAllMessages(); + verify(listener).checkSave(any(Runnable.class), eq(mSbn)); + } @Test public void testCloseControls_blockingHelperDismissedIfShown() throws Exception { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index e2e5b3279854..8340ab2e7935 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -162,6 +162,14 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { verify(mBouncer, never()).show(eq(false), eq(false)); } + @Test + public void onPanelExpansionChanged_neverTranslatesBouncerWhenOccluded() { + mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animate */); + mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */, + true /* tracking */); + verify(mBouncer, never()).setExpansion(eq(0.5f)); + } + private class TestableStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager { public TestableStatusBarKeyguardViewManager(Context context, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java new file mode 100644 index 000000000000..3b47eae03fa0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java @@ -0,0 +1,93 @@ +/* + * 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.statusbar.policy; + +import static com.google.common.truth.Truth.assertThat; + +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.content.res.Configuration; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.CommandQueue; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class RemoteInputQuickSettingsDisablerTest extends SysuiTestCase { + + private CommandQueue mCommandQueue; + private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mCommandQueue = mock(CommandQueue.class); + mContext.putComponent(CommandQueue.class, mCommandQueue); + + mRemoteInputQuickSettingsDisabler = new RemoteInputQuickSettingsDisabler(mContext); + } + + @Test + public void shouldEnableQuickSetting_afterDeactiviate() { + mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.TRUE); + mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.FALSE); + assertFalse(mRemoteInputQuickSettingsDisabler.mRemoteInputActive); + verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean()); + } + + @Test + public void shouldDisableQuickSetting_afteActiviate() { + mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.FALSE); + mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.TRUE); + assertTrue(mRemoteInputQuickSettingsDisabler.mRemoteInputActive); + verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean()); + } + + @Test + public void testChangeToLandscape() { + Configuration c = new Configuration(mContext.getResources().getConfiguration()); + c.orientation = Configuration.ORIENTATION_PORTRAIT; + mRemoteInputQuickSettingsDisabler.onConfigChanged(c); + c.orientation = Configuration.ORIENTATION_LANDSCAPE; + mRemoteInputQuickSettingsDisabler.onConfigChanged(c); + assertTrue(mRemoteInputQuickSettingsDisabler.misLandscape); + verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean()); + } + + @Test + public void testChangeToPortrait() { + Configuration c = new Configuration(mContext.getResources().getConfiguration()); + c.orientation = Configuration.ORIENTATION_LANDSCAPE; + mRemoteInputQuickSettingsDisabler.onConfigChanged(c); + c.orientation = Configuration.ORIENTATION_PORTRAIT; + mRemoteInputQuickSettingsDisabler.onConfigChanged(c); + assertFalse(mRemoteInputQuickSettingsDisabler.misLandscape); + verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean()); + } + +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java index 938dfca14754..a6fa4f54067d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java @@ -54,6 +54,7 @@ public class RemoteInputViewTest extends SysuiTestCase { @Mock private RemoteInputController mController; @Mock private ShortcutManager mShortcutManager; + @Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; private BlockingQueueIntentReceiver mReceiver; private RemoteInputView mView; @@ -61,6 +62,9 @@ public class RemoteInputViewTest extends SysuiTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mDependency.injectTestDependency(RemoteInputQuickSettingsDisabler.class, + mRemoteInputQuickSettingsDisabler); + mReceiver = new BlockingQueueIntentReceiver(); mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION), null, Handler.createAsync(Dependency.get(Dependency.BG_LOOPER))); diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto index 9ff8e7dbeb92..bf9ccb838ae9 100644 --- a/proto/src/wifi.proto +++ b/proto/src/wifi.proto @@ -453,6 +453,10 @@ message WifiLog { // Number of times the firmware picked a SoftAp channel not satisfying user band preference. optional int32 num_soft_ap_user_band_preference_unsatisfied = 116; + + // Identifier for experimental scoring parameter settings. + optional string score_experiment_id = 117; + } // Information that gets logged for every WiFi connection. diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index fa2a0d9ad837..a5311b28c32e 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -53,7 +53,6 @@ import android.view.WindowManager; import android.view.autofill.AutofillManager; import android.widget.ImageView; import android.widget.RemoteViews; -import android.widget.ScrollView; import android.widget.TextView; import com.android.internal.R; @@ -208,7 +207,7 @@ final class SaveUi { mSubTitle = info.getDescription(); if (mSubTitle != null) { writeLog(MetricsEvent.AUTOFILL_SAVE_CUSTOM_SUBTITLE, type); - final ScrollView subtitleContainer = + final ViewGroup subtitleContainer = view.findViewById(R.id.autofill_save_custom_subtitle); final TextView subtitleView = new TextView(context); subtitleView.setText(mSubTitle); @@ -361,7 +360,7 @@ final class SaveUi { } // Finally, add the custom description to the save UI. - final ScrollView subtitleContainer = + final ViewGroup subtitleContainer = saveUiView.findViewById(R.id.autofill_save_custom_subtitle); subtitleContainer.addView(customSubtitleView); subtitleContainer.setVisibility(View.VISIBLE); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 797cb4b53ecb..e22a86e39d91 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2503,6 +2503,9 @@ public class ConnectivityService extends IConnectivityManager.Stub ensureNetworkTransitionWakelock(nai.name()); } mLegacyTypeTracker.remove(nai, wasDefault); + if (!nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) { + updateAllVpnsCapabilities(); + } rematchAllNetworksAndRequests(null, 0); mLingerMonitor.noteDisconnect(nai); if (nai.created) { @@ -3778,6 +3781,26 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + /** + * Ask all VPN objects to recompute and update their capabilities. + * + * When underlying networks change, VPNs may have to update capabilities to reflect things + * like the metered bit, their transports, and so on. This asks the VPN objects to update + * their capabilities, and as this will cause them to send messages to the ConnectivityService + * handler thread through their agent, this is asynchronous. When the capabilities objects + * are computed they will be up-to-date as they are computed synchronously from here and + * this is running on the ConnectivityService thread. + * TODO : Fix this and call updateCapabilities inline to remove out-of-order events. + */ + private void updateAllVpnsCapabilities() { + synchronized (mVpns) { + for (int i = 0; i < mVpns.size(); i++) { + final Vpn vpn = mVpns.valueAt(i); + vpn.updateCapabilities(); + } + } + } + @Override public boolean updateLockdownVpn() { if (Binder.getCallingUid() != Process.SYSTEM_UID) { @@ -4575,7 +4598,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Note: if mDefaultRequest is changed, NetworkMonitor needs to be updated. private final NetworkRequest mDefaultRequest; - + // Request used to optionally keep mobile data active even when higher // priority networks like Wi-Fi are active. private final NetworkRequest mDefaultMobileDataRequest; @@ -4651,7 +4674,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) { - LinkProperties newLp = networkAgent.linkProperties; + LinkProperties newLp = new LinkProperties(networkAgent.linkProperties); int netId = networkAgent.network.netId; // The NetworkAgentInfo does not know whether clatd is running on its network or not. Before @@ -4685,6 +4708,9 @@ public class ConnectivityService extends IConnectivityManager.Stub } // TODO - move this check to cover the whole function if (!Objects.equals(newLp, oldLp)) { + synchronized (networkAgent) { + networkAgent.linkProperties = newLp; + } notifyIfacesChangedForNetworkStats(); notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED); } @@ -4935,12 +4961,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!newNc.hasTransport(TRANSPORT_VPN)) { // Tell VPNs about updated capabilities, since they may need to // bubble those changes through. - synchronized (mVpns) { - for (int i = 0; i < mVpns.size(); i++) { - final Vpn vpn = mVpns.valueAt(i); - vpn.updateCapabilities(); - } - } + updateAllVpnsCapabilities(); } } diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java index 2869114601b7..5a25f48c38e3 100644 --- a/services/core/java/com/android/server/PinnerService.java +++ b/services/core/java/com/android/server/PinnerService.java @@ -16,6 +16,8 @@ package com.android.server; +import android.annotation.Nullable; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -47,10 +49,18 @@ import dalvik.system.DexFile; import dalvik.system.VMRuntime; import java.io.FileDescriptor; +import java.io.Closeable; +import java.io.InputStream; +import java.io.DataInputStream; import java.io.IOException; +import java.io.EOFException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.zip.ZipFile; +import java.util.zip.ZipException; +import java.util.zip.ZipEntry; + /** * <p>PinnerService pins important files for key processes in memory.</p> * <p>Files to pin are specified in the config_defaultPinnerServiceFiles @@ -60,16 +70,18 @@ import java.util.ArrayList; public final class PinnerService extends SystemService { private static final boolean DEBUG = false; private static final String TAG = "PinnerService"; + private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); //80MB max + private static final String PIN_META_FILENAME = "pinlist.meta"; + private static final int PAGE_SIZE = (int) Os.sysconf(OsConstants._SC_PAGESIZE); private final Context mContext; + private final boolean mShouldPinCamera; + + /* These lists protected by PinnerService monitor lock */ private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<PinnedFile>(); private final ArrayList<PinnedFile> mPinnedCameraFiles = new ArrayList<PinnedFile>(); - private final boolean mShouldPinCamera; private BinderService mBinderService; - - private final long MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); //80MB max - private PinnerHandler mPinnerHandler = null; private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @@ -146,64 +158,43 @@ public final class PinnerService extends SystemService { // Files to pin come from the overlay and can be specified per-device config String[] filesToPin = mContext.getResources().getStringArray( com.android.internal.R.array.config_defaultPinnerServiceFiles); - synchronized(this) { - // Continue trying to pin remaining files even if there is a failure - for (int i = 0; i < filesToPin.length; i++){ - PinnedFile pf = pinFile(filesToPin[i], 0, 0, 0); - if (pf != null) { - mPinnedFiles.add(pf); - if (DEBUG) { - Slog.i(TAG, "Pinned file = " + pf.mFilename); - } - } else { - Slog.e(TAG, "Failed to pin file = " + filesToPin[i]); - } + // Continue trying to pin each file even if we fail to pin some of them + for (String fileToPin : filesToPin) { + PinnedFile pf = pinFile(fileToPin, + Integer.MAX_VALUE, + /*attemptPinIntrospection=*/false); + if (pf == null) { + Slog.e(TAG, "Failed to pin file = " + fileToPin); + continue; } - } - } - /** - * Handler for camera pinning message - */ - private void handlePinCamera(int userHandle) { - if (mShouldPinCamera) { - synchronized(this) { - boolean success = pinCamera(userHandle); - if (!success) { - //this is not necessarily an error - if (DEBUG) { - Slog.v(TAG, "Failed to pin camera."); - } - } + synchronized (this) { + mPinnedFiles.add(pf); } } } /** - * determine if the camera app is already pinned by comparing the - * intent resolution to the pinned files list + * Handler for camera pinning message */ - private boolean alreadyPinned(int userHandle) { - ApplicationInfo cameraInfo = getCameraInfo(userHandle); - if (cameraInfo == null ) { - return false; - } - for (int i = 0; i < mPinnedCameraFiles.size(); i++) { - if (mPinnedCameraFiles.get(i).mFilename.equals(cameraInfo.sourceDir)) { - if (DEBUG) { - Slog.v(TAG, "Camera is already pinned"); - } - return true; + private void handlePinCamera(int userHandle) { + if (!mShouldPinCamera) return; + if (!pinCamera(userHandle)) { + if (DEBUG) { + Slog.v(TAG, "Failed to pin camera."); } } - return false; } private void unpinCameraApp() { - for (int i = 0; i < mPinnedCameraFiles.size(); i++) { - unpinFile(mPinnedCameraFiles.get(i)); + ArrayList<PinnedFile> pinnedCameraFiles; + synchronized (this) { + pinnedCameraFiles = new ArrayList<>(mPinnedCameraFiles); + mPinnedCameraFiles.clear(); + } + for (PinnedFile pinnedFile : pinnedCameraFiles) { + pinnedFile.close(); } - mPinnedCameraFiles.clear(); } private boolean isResolverActivity(ActivityInfo info) { @@ -255,15 +246,19 @@ public final class PinnerService extends SystemService { //pin APK String camAPK = cameraInfo.sourceDir; - PinnedFile pf = pinFile(camAPK, 0, 0, MAX_CAMERA_PIN_SIZE); + PinnedFile pf = pinFile(camAPK, + MAX_CAMERA_PIN_SIZE, + /*attemptPinIntrospection=*/true); if (pf == null) { Slog.e(TAG, "Failed to pin " + camAPK); return false; } if (DEBUG) { - Slog.i(TAG, "Pinned " + pf.mFilename); + Slog.i(TAG, "Pinned " + pf.fileName); + } + synchronized (this) { + mPinnedCameraFiles.add(pf); } - mPinnedCameraFiles.add(pf); // determine the ABI from either ApplicationInfo or Build String arch = "arm"; @@ -289,11 +284,13 @@ public final class PinnerService extends SystemService { //not pinning the oat/odex is not a fatal error for (String file : files) { - pf = pinFile(file, 0, 0, MAX_CAMERA_PIN_SIZE); + pf = pinFile(file, MAX_CAMERA_PIN_SIZE, /*attemptPinIntrospection=*/false); if (pf != null) { - mPinnedCameraFiles.add(pf); + synchronized (this) { + mPinnedCameraFiles.add(pf); + } if (DEBUG) { - Slog.i(TAG, "Pinned " + pf.mFilename); + Slog.i(TAG, "Pinned " + pf.fileName); } } } @@ -302,70 +299,294 @@ public final class PinnerService extends SystemService { } - /** mlock length bytes of fileToPin in memory, starting at offset - * length == 0 means pin from offset to end of file - * maxSize == 0 means infinite + /** mlock length bytes of fileToPin in memory + * + * If attemptPinIntrospection is true, then treat the file to pin as a zip file and + * look for a "pinlist.meta" file in the archive root directory. The structure of this + * file is a PINLIST_META as described below: + * + * <pre> + * PINLIST_META: PIN_RANGE* + * PIN_RANGE: PIN_START PIN_LENGTH + * PIN_START: big endian i32: offset in bytes of pin region from file start + * PIN_LENGTH: big endian i32: length of pin region in bytes + * </pre> + * + * (We use big endian because that's what DataInputStream is hardcoded to use.) + * + * If attemptPinIntrospection is false, then we use a single implicit PIN_RANGE of (0, + * maxBytesToPin); that is, we attempt to pin the first maxBytesToPin bytes of the file. + * + * After we open a file, we march through the list of pin ranges and attempt to pin + * each one, stopping after we've pinned maxBytesToPin bytes. (We may truncate the last + * pinned range to fit.) In this way, by choosing to emit certain PIN_RANGE pairs + * before others, file generators can express pins in priority order, making most + * effective use of the pinned-page quota. + * + * N.B. Each PIN_RANGE is clamped to the actual bounds of the file; all inputs have a + * meaningful interpretation. Also, a range locking a single byte of a page locks the + * whole page. Any truncated PIN_RANGE at EOF is ignored. Overlapping pinned entries + * are legal, but each pin of a byte counts toward the pin quota regardless of whether + * that byte has already been pinned, so the generator of PINLIST_META ought to ensure + * that ranges are non-overlapping. + * + * @param fileToPin Path to file to pin + * @param maxBytesToPin Maximum number of bytes to pin + * @param attemptPinIntrospection If true, try to open file as a + * zip in order to extract the + * @return Pinned memory resource owner thing or null on error */ - private static PinnedFile pinFile(String fileToPin, long offset, long length, long maxSize) { - FileDescriptor fd = new FileDescriptor(); + private static PinnedFile pinFile(String fileToPin, + int maxBytesToPin, + boolean attemptPinIntrospection) { + ZipFile fileAsZip = null; + InputStream pinRangeStream = null; try { - fd = Os.open(fileToPin, - OsConstants.O_RDONLY | OsConstants.O_CLOEXEC | OsConstants.O_NOFOLLOW, - OsConstants.O_RDONLY); - - StructStat sb = Os.fstat(fd); - - if (offset + length > sb.st_size) { - Os.close(fd); - Slog.e(TAG, "Failed to pin file " + fileToPin + - ", request extends beyond end of file. offset + length = " - + (offset + length) + ", file length = " + sb.st_size); - return null; + if (attemptPinIntrospection) { + fileAsZip = maybeOpenZip(fileToPin); } - if (length == 0) { - length = sb.st_size - offset; + if (fileAsZip != null) { + pinRangeStream = maybeOpenPinMetaInZip(fileAsZip, fileToPin); } - if (maxSize > 0 && length > maxSize) { - Slog.e(TAG, "Could not pin file " + fileToPin + - ", size = " + length + ", maxSize = " + maxSize); - Os.close(fd); - return null; + Slog.d(TAG, "pinRangeStream: " + pinRangeStream); + + PinRangeSource pinRangeSource = (pinRangeStream != null) + ? new PinRangeSourceStream(pinRangeStream) + : new PinRangeSourceStatic(0, Integer.MAX_VALUE /* will be clipped */); + return pinFileRanges(fileToPin, maxBytesToPin, pinRangeSource); + } finally { + safeClose(pinRangeStream); + safeClose(fileAsZip); // Also closes any streams we've opened + } + } + + /** + * Attempt to open a file as a zip file. On any sort of corruption, log, swallow the + * error, and return null. + */ + private static ZipFile maybeOpenZip(String fileName) { + ZipFile zip = null; + try { + zip = new ZipFile(fileName); + } catch (IOException ex) { + Slog.w(TAG, + String.format( + "could not open \"%s\" as zip: pinning as blob", + fileName), + ex); + } + return zip; + } + + /** + * Open a pin metadata file in the zip if one is present. + * + * @param zipFile Zip file to search + * @return Open input stream or null on any error + */ + private static InputStream maybeOpenPinMetaInZip(ZipFile zipFile, String fileName) { + ZipEntry pinMetaEntry = zipFile.getEntry(PIN_META_FILENAME); + InputStream pinMetaStream = null; + if (pinMetaEntry != null) { + try { + pinMetaStream = zipFile.getInputStream(pinMetaEntry); + } catch (IOException ex) { + Slog.w(TAG, + String.format("error reading pin metadata \"%s\": pinning as blob", + fileName), + ex); } + } + return pinMetaStream; + } + + private static abstract class PinRangeSource { + /** Retrive a range to pin. + * + * @param outPinRange Receives the pin region + * @return True if we filled in outPinRange or false if we're out of pin entries + */ + abstract boolean read(PinRange outPinRange); + } + + private static final class PinRangeSourceStatic extends PinRangeSource { + private final int mPinStart; + private final int mPinLength; + private boolean mDone = false; + + PinRangeSourceStatic(int pinStart, int pinLength) { + mPinStart = pinStart; + mPinLength = pinLength; + } - long address = Os.mmap(0, length, OsConstants.PROT_READ, - OsConstants.MAP_PRIVATE, fd, offset); - Os.close(fd); + @Override + boolean read(PinRange outPinRange) { + outPinRange.start = mPinStart; + outPinRange.length = mPinLength; + boolean done = mDone; + mDone = true; + return !done; + } + } - Os.mlock(address, length); + private static final class PinRangeSourceStream extends PinRangeSource { + private final DataInputStream mStream; + private boolean mDone = false; - return new PinnedFile(address, length, fileToPin); - } catch (ErrnoException e) { - Slog.e(TAG, "Could not pin file " + fileToPin + " with error " + e.getMessage()); - if(fd.valid()) { + PinRangeSourceStream(InputStream stream) { + mStream = new DataInputStream(stream); + } + + @Override + boolean read(PinRange outPinRange) { + if (!mDone) { try { - Os.close(fd); + outPinRange.start = mStream.readInt(); + outPinRange.length = mStream.readInt(); + } catch (IOException ex) { + mDone = true; } - catch (ErrnoException eClose) { - Slog.e(TAG, "Failed to close fd, error = " + eClose.getMessage()); + } + return !mDone; + } + } + + /** + * Helper for pinFile. + * + * @param fileToPin Name of file to pin + * @param maxBytesToPin Maximum number of bytes to pin + * @param pinRangeSource Read PIN_RANGE entries from this stream to tell us what bytes + * to pin. + * @return PinnedFile or null on error + */ + private static PinnedFile pinFileRanges( + String fileToPin, + int maxBytesToPin, + PinRangeSource pinRangeSource) + { + FileDescriptor fd = new FileDescriptor(); + long address = -1; + int mapSize = 0; + + try { + int openFlags = (OsConstants.O_RDONLY | + OsConstants.O_CLOEXEC | + OsConstants.O_NOFOLLOW); + fd = Os.open(fileToPin, openFlags, 0); + mapSize = (int) Math.min(Os.fstat(fd).st_size, Integer.MAX_VALUE); + address = Os.mmap(0, mapSize, + OsConstants.PROT_READ, + OsConstants.MAP_SHARED, + fd, /*offset=*/0); + + PinRange pinRange = new PinRange(); + int bytesPinned = 0; + + // We pin at page granularity, so make sure the limit is page-aligned + if (maxBytesToPin % PAGE_SIZE != 0) { + maxBytesToPin -= maxBytesToPin % PAGE_SIZE; + } + + while (bytesPinned < maxBytesToPin && pinRangeSource.read(pinRange)) { + int pinStart = pinRange.start; + int pinLength = pinRange.length; + pinStart = clamp(0, pinStart, mapSize); + pinLength = clamp(0, pinLength, mapSize - pinStart); + pinLength = Math.min(maxBytesToPin - bytesPinned, pinLength); + + // mlock doesn't require the region to be page-aligned, but we snap the + // lock region to page boundaries anyway so that we don't under-count + // locking a single byte of a page as a charge of one byte even though the + // OS will retain the whole page. Thanks to this adjustment, we slightly + // over-count the pin charge of back-to-back pins touching the same page, + // but better that than undercounting. Besides: nothing stops pin metafile + // creators from making the actual regions page-aligned. + pinLength += pinStart % PAGE_SIZE; + pinStart -= pinStart % PAGE_SIZE; + if (pinLength % PAGE_SIZE != 0) { + pinLength += PAGE_SIZE - pinLength % PAGE_SIZE; + } + pinLength = clamp(0, pinLength, maxBytesToPin - bytesPinned); + + if (pinLength > 0) { + if (DEBUG) { + Slog.d(TAG, + String.format( + "pinning at %s %s bytes of %s", + pinStart, pinLength, fileToPin)); + } + Os.mlock(address + pinStart, pinLength); } + bytesPinned += pinLength; } + + PinnedFile pinnedFile = new PinnedFile(address, mapSize, fileToPin, bytesPinned); + address = -1; // Ownership transferred + return pinnedFile; + } catch (ErrnoException ex) { + Slog.e(TAG, "Could not pin file " + fileToPin, ex); return null; + } finally { + safeClose(fd); + if (address >= 0) { + safeMunmap(address, mapSize); + } } } - private static boolean unpinFile(PinnedFile pf) { + private static int clamp(int min, int value, int max) { + return Math.max(min, Math.min(value, max)); + } + + private static void safeMunmap(long address, long mapSize) { try { - Os.munlock(pf.mAddress, pf.mLength); - } catch (ErrnoException e) { - Slog.e(TAG, "Failed to unpin file " + pf.mFilename + " with error " + e.getMessage()); - return false; + Os.munmap(address, mapSize); + } catch (ErrnoException ex) { + Slog.w(TAG, "ignoring error in unmap", ex); } - if (DEBUG) { - Slog.i(TAG, "Unpinned file " + pf.mFilename ); + } + + /** + * Close FD, swallowing irrelevant errors. + */ + private static void safeClose(@Nullable FileDescriptor fd) { + if (fd != null && fd.valid()) { + try { + Os.close(fd); + } catch (ErrnoException ex) { + // Swallow the exception: non-EBADF errors in close(2) + // indicate deferred paging write errors, which we + // don't care about here. The underlying file + // descriptor is always closed. + if (ex.errno == OsConstants.EBADF) { + throw new AssertionError(ex); + } + } + } + } + + /** + * Close closeable thing, swallowing errors. + */ + private static void safeClose(@Nullable Closeable thing) { + if (thing != null) { + try { + thing.close(); + } catch (IOException ex) { + Slog.w(TAG, "ignoring error closing resource: " + thing, ex); + } } - return true; + } + + private synchronized ArrayList<PinnedFile> snapshotPinnedFiles() { + int nrPinnedFiles = mPinnedFiles.size() + mPinnedCameraFiles.size(); + ArrayList<PinnedFile> pinnedFiles = new ArrayList<>(nrPinnedFiles); + pinnedFiles.addAll(mPinnedFiles); + pinnedFiles.addAll(mPinnedCameraFiles); + return pinnedFiles; } private final class BinderService extends Binder { @@ -373,31 +594,44 @@ public final class PinnerService extends SystemService { protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; long totalSize = 0; - pw.println("Pinned Files:"); - synchronized(this) { - for (int i = 0; i < mPinnedFiles.size(); i++) { - pw.println(mPinnedFiles.get(i).mFilename); - totalSize += mPinnedFiles.get(i).mLength; - } - for (int i = 0; i < mPinnedCameraFiles.size(); i++) { - pw.println(mPinnedCameraFiles.get(i).mFilename); - totalSize += mPinnedCameraFiles.get(i).mLength; - } + for (PinnedFile pinnedFile : snapshotPinnedFiles()) { + pw.format("%s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned); + totalSize += pinnedFile.bytesPinned; } - pw.println("Total size: " + totalSize); + pw.format("Total size: %s\n", totalSize); } } - private static class PinnedFile { - long mAddress; - long mLength; - String mFilename; + private static final class PinnedFile implements AutoCloseable { + private long mAddress; + final int mapSize; + final String fileName; + final int bytesPinned; - PinnedFile(long address, long length, String filename) { + PinnedFile(long address, int mapSize, String fileName, int bytesPinned) { mAddress = address; - mLength = length; - mFilename = filename; + this.mapSize = mapSize; + this.fileName = fileName; + this.bytesPinned = bytesPinned; } + + @Override + public void close() { + if (mAddress >= 0) { + safeMunmap(mAddress, mapSize); + mAddress = -1; + } + } + + @Override + public void finalize() { + close(); + } + } + + final static class PinRange { + int start; + int length; } final class PinnerHandler extends Handler { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 113f452f220a..98bf740d0d30 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -15952,7 +15952,10 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void getMyMemoryState(ActivityManager.RunningAppProcessInfo outInfo) { + public void getMyMemoryState(ActivityManager.RunningAppProcessInfo outState) { + if (outState == null) { + throw new IllegalArgumentException("outState is null"); + } enforceNotIsolatedCaller("getMyMemoryState"); final int callingUid = Binder.getCallingUid(); @@ -15963,7 +15966,9 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (mPidsSelfLocked) { proc = mPidsSelfLocked.get(Binder.getCallingPid()); } - fillInProcMemInfo(proc, outInfo, clientTargetSdk); + if (proc != null) { + fillInProcMemInfo(proc, outState, clientTargetSdk); + } } } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 73710d3d39d7..d64b429edcd4 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -1077,6 +1077,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // System process can launch home activity. return true; } + // Allow the recents component to launch the home activity. + final RecentTasks recentTasks = mStackSupervisor.mService.getRecentTasks(); + if (recentTasks != null && recentTasks.isCallerRecents(uid)) { + return true; + } // Resolver activity can launch home activity. return sourceRecord != null && sourceRecord.isResolverActivity(); } diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java index c81624a87b4e..9b9a3801aea1 100644 --- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java @@ -19,7 +19,11 @@ package com.android.server.connectivity; import static android.net.CaptivePortal.APP_RETURN_DISMISSED; import static android.net.CaptivePortal.APP_RETURN_UNWANTED; import static android.net.CaptivePortal.APP_RETURN_WANTED_AS_IS; +import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_PROBE_SPEC; +import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL; +import static android.net.metrics.ValidationProbeEvent.PROBE_FALLBACK; +import android.annotation.Nullable; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; @@ -34,6 +38,8 @@ import android.net.NetworkRequest; import android.net.ProxyInfo; import android.net.TrafficStats; import android.net.Uri; +import android.net.captiveportal.CaptivePortalProbeResult; +import android.net.captiveportal.CaptivePortalProbeSpec; import android.net.dns.ResolvUtil; import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; @@ -62,6 +68,7 @@ import android.util.LocalLog.ReadOnlyLocalLog; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; @@ -258,6 +265,8 @@ public class NetworkMonitor extends StateMachine { private final URL mCaptivePortalHttpsUrl; private final URL mCaptivePortalHttpUrl; private final URL[] mCaptivePortalFallbackUrls; + @Nullable + private final CaptivePortalProbeSpec[] mCaptivePortalFallbackSpecs; @VisibleForTesting protected boolean mIsCaptivePortalCheckEnabled; @@ -333,6 +342,7 @@ public class NetworkMonitor extends StateMachine { mCaptivePortalHttpsUrl = makeURL(getCaptivePortalServerHttpsUrl()); mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl(settings, context)); mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls(); + mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs(); start(); } @@ -541,8 +551,12 @@ public class NetworkMonitor extends StateMachine { sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response); } })); - intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL, - mLastPortalProbeResult.detectUrl); + final CaptivePortalProbeResult probeRes = mLastPortalProbeResult; + intent.putExtra(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl); + if (probeRes.probeSpec != null) { + final String encodedSpec = probeRes.probeSpec.getEncodedSpec(); + intent.putExtra(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec); + } intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT, mCaptivePortalUserAgent); intent.setFlags( @@ -561,47 +575,6 @@ public class NetworkMonitor extends StateMachine { } } - /** - * Result of calling isCaptivePortal(). - * @hide - */ - @VisibleForTesting - public static final class CaptivePortalProbeResult { - static final int SUCCESS_CODE = 204; - static final int FAILED_CODE = 599; - - static final CaptivePortalProbeResult FAILED = new CaptivePortalProbeResult(FAILED_CODE); - static final CaptivePortalProbeResult SUCCESS = new CaptivePortalProbeResult(SUCCESS_CODE); - - private final int mHttpResponseCode; // HTTP response code returned from Internet probe. - final String redirectUrl; // Redirect destination returned from Internet probe. - final String detectUrl; // URL where a 204 response code indicates - // captive portal has been appeased. - - public CaptivePortalProbeResult( - int httpResponseCode, String redirectUrl, String detectUrl) { - mHttpResponseCode = httpResponseCode; - this.redirectUrl = redirectUrl; - this.detectUrl = detectUrl; - } - - public CaptivePortalProbeResult(int httpResponseCode) { - this(httpResponseCode, null, null); - } - - boolean isSuccessful() { - return mHttpResponseCode == SUCCESS_CODE; - } - - boolean isPortal() { - return !isSuccessful() && (mHttpResponseCode >= 200) && (mHttpResponseCode <= 399); - } - - boolean isFailed() { - return !isSuccessful() && !isPortal(); - } - } - // Being in the EvaluatingState State indicates the Network is being evaluated for internet // connectivity, or that the user has indicated that this network is unwanted. private class EvaluatingState extends State { @@ -822,8 +795,9 @@ public class NetworkMonitor extends StateMachine { private void resolveStrictModeHostname() { try { // Do a blocking DNS resolution using the network-assigned nameservers. + // Do not set AI_ADDRCONFIG in ai_flags so we get all address families in advance. final InetAddress[] ips = ResolvUtil.blockingResolveAllLocally( - mNetwork, mPrivateDnsProviderHostname); + mNetwork, mPrivateDnsProviderHostname, 0 /* aiFlags */); mPrivateDnsConfig = new PrivateDnsConfig(mPrivateDnsProviderHostname, ips); } catch (UnknownHostException uhe) { mPrivateDnsConfig = null; @@ -922,23 +896,47 @@ public class NetworkMonitor extends StateMachine { } private URL[] makeCaptivePortalFallbackUrls() { - String separator = ","; - String firstUrl = mSettings.getSetting(mContext, - Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, DEFAULT_FALLBACK_URL); - String joinedUrls = firstUrl + separator + mSettings.getSetting(mContext, - Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, DEFAULT_OTHER_FALLBACK_URLS); - List<URL> urls = new ArrayList<>(); - for (String s : joinedUrls.split(separator)) { - URL u = makeURL(s); - if (u == null) { - continue; + try { + String separator = ","; + String firstUrl = mSettings.getSetting(mContext, + Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, DEFAULT_FALLBACK_URL); + String joinedUrls = firstUrl + separator + mSettings.getSetting(mContext, + Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, + DEFAULT_OTHER_FALLBACK_URLS); + List<URL> urls = new ArrayList<>(); + for (String s : joinedUrls.split(separator)) { + URL u = makeURL(s); + if (u == null) { + continue; + } + urls.add(u); + } + if (urls.isEmpty()) { + Log.e(TAG, String.format("could not create any url from %s", joinedUrls)); } - urls.add(u); + return urls.toArray(new URL[urls.size()]); + } catch (Exception e) { + // Don't let a misconfiguration bootloop the system. + Log.e(TAG, "Error parsing configured fallback URLs", e); + return new URL[0]; } - if (urls.isEmpty()) { - Log.e(TAG, String.format("could not create any url from %s", joinedUrls)); + } + + private CaptivePortalProbeSpec[] makeCaptivePortalFallbackProbeSpecs() { + try { + final String settingsValue = mSettings.getSetting( + mContext, Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS, null); + // Probe specs only used if configured in settings + if (TextUtils.isEmpty(settingsValue)) { + return null; + } + + return CaptivePortalProbeSpec.parseCaptivePortalProbeSpecs(settingsValue); + } catch (Exception e) { + // Don't let a misconfiguration bootloop the system. + Log.e(TAG, "Error parsing configured fallback probe specs", e); + return null; } - return urls.toArray(new URL[urls.size()]); } private String getCaptivePortalUserAgent() { @@ -955,6 +953,15 @@ public class NetworkMonitor extends StateMachine { return mCaptivePortalFallbackUrls[idx]; } + private CaptivePortalProbeSpec nextFallbackSpec() { + if (ArrayUtils.isEmpty(mCaptivePortalFallbackSpecs)) { + return null; + } + // Randomly change spec without memory. Also randomize the first attempt. + final int idx = Math.abs(new Random().nextInt()) % mCaptivePortalFallbackSpecs.length; + return mCaptivePortalFallbackSpecs[idx]; + } + @VisibleForTesting protected CaptivePortalProbeResult isCaptivePortal() { if (!mIsCaptivePortalCheckEnabled) { @@ -1025,7 +1032,7 @@ public class NetworkMonitor extends StateMachine { // unnecessary resolution. final String host = (proxy != null) ? proxy.getHost() : url.getHost(); sendDnsProbe(host); - return sendHttpProbe(url, probeType); + return sendHttpProbe(url, probeType, null); } /** Do a DNS resolution of the given server. */ @@ -1061,7 +1068,8 @@ public class NetworkMonitor extends StateMachine { * @return a CaptivePortalProbeResult inferred from the HTTP response. */ @VisibleForTesting - protected CaptivePortalProbeResult sendHttpProbe(URL url, int probeType) { + protected CaptivePortalProbeResult sendHttpProbe(URL url, int probeType, + @Nullable CaptivePortalProbeSpec probeSpec) { HttpURLConnection urlConnection = null; int httpResponseCode = CaptivePortalProbeResult.FAILED_CODE; String redirectUrl = null; @@ -1133,7 +1141,12 @@ public class NetworkMonitor extends StateMachine { TrafficStats.setThreadStatsTag(oldTag); } logValidationProbe(probeTimer.stop(), probeType, httpResponseCode); - return new CaptivePortalProbeResult(httpResponseCode, redirectUrl, url.toString()); + + if (probeSpec == null) { + return new CaptivePortalProbeResult(httpResponseCode, redirectUrl, url.toString()); + } else { + return probeSpec.getResult(httpResponseCode, redirectUrl); + } } private CaptivePortalProbeResult sendParallelHttpProbes( @@ -1196,11 +1209,12 @@ public class NetworkMonitor extends StateMachine { if (httpsResult.isPortal() || httpsResult.isSuccessful()) { return httpsResult; } - // If a fallback url exists, use a fallback probe to try again portal detection. - URL fallbackUrl = nextFallbackUrl(); + // If a fallback method exists, use it to retry portal detection. + // If we have new-style probe specs, use those. Otherwise, use the fallback URLs. + final CaptivePortalProbeSpec probeSpec = nextFallbackSpec(); + final URL fallbackUrl = (probeSpec != null) ? probeSpec.getUrl() : nextFallbackUrl(); if (fallbackUrl != null) { - CaptivePortalProbeResult result = - sendHttpProbe(fallbackUrl, ValidationProbeEvent.PROBE_FALLBACK); + CaptivePortalProbeResult result = sendHttpProbe(fallbackUrl, PROBE_FALLBACK, probeSpec); if (result.isPortal()) { return result; } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 2fda08e2a575..a91989838521 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -172,10 +172,13 @@ public class Vpn { private PendingIntent mStatusIntent; private volatile boolean mEnableTeardown = true; private final INetworkManagementService mNetd; - private VpnConfig mConfig; - private NetworkAgent mNetworkAgent; + @VisibleForTesting + protected VpnConfig mConfig; + @VisibleForTesting + protected NetworkAgent mNetworkAgent; private final Looper mLooper; - private final NetworkCapabilities mNetworkCapabilities; + @VisibleForTesting + protected final NetworkCapabilities mNetworkCapabilities; private final SystemServices mSystemServices; /** @@ -316,15 +319,12 @@ public class Vpn { boolean roaming = false; boolean congested = false; - if (ArrayUtils.isEmpty(underlyingNetworks)) { - // No idea what the underlying networks are; assume sane defaults - metered = true; - roaming = false; - congested = false; - } else { + boolean hadUnderlyingNetworks = false; + if (null != underlyingNetworks) { for (Network underlying : underlyingNetworks) { final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying); if (underlyingCaps == null) continue; + hadUnderlyingNetworks = true; for (int underlyingType : underlyingCaps.getTransportTypes()) { transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType); } @@ -340,6 +340,12 @@ public class Vpn { congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED); } } + if (!hadUnderlyingNetworks) { + // No idea what the underlying networks are; assume sane defaults + metered = true; + roaming = false; + congested = false; + } caps.setTransportTypes(transportTypes); caps.setLinkDownstreamBandwidthKbps(downKbps); @@ -1071,7 +1077,8 @@ public class Vpn { // Returns true if the VPN has been established and the calling UID is its owner. Used to check // that a call to mutate VPN state is admissible. - private boolean isCallerEstablishedOwnerLocked() { + @VisibleForTesting + protected boolean isCallerEstablishedOwnerLocked() { return isRunningLocked() && Binder.getCallingUid() == mOwnerUID; } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 1be507e4b9c0..99412c56b274 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -489,6 +489,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mTemporaryScreenBrightness = -1; mPendingScreenBrightnessSetting = -1; mTemporaryAutoBrightnessAdjustment = Float.NaN; + mPendingAutoBrightnessAdjustment = Float.NaN; } /** @@ -815,7 +816,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAppliedTemporaryAutoBrightnessAdjustment = true; } else { autoBrightnessAdjustment = mAutoBrightnessAdjustment; - mAppliedTemporaryAutoBrightnessAdjustment = true; + mAppliedTemporaryAutoBrightnessAdjustment = false; } // Apply brightness boost. @@ -1499,6 +1500,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call return false; } if (mAutoBrightnessAdjustment == mPendingAutoBrightnessAdjustment) { + mPendingAutoBrightnessAdjustment = Float.NaN; return false; } mAutoBrightnessAdjustment = mPendingAutoBrightnessAdjustment; diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java index 2a6f7c8eaadb..06329e571b43 100644 --- a/services/core/java/com/android/server/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -1500,7 +1500,10 @@ public class FingerprintService extends SystemService implements IHwBinder.Death userId = getUserOrWorkProfileId(clientPackage, userId); if (userId != mCurrentUserId) { int firstSdkInt = Build.VERSION.FIRST_SDK_INT; - if (firstSdkInt == 0) firstSdkInt = Build.VERSION.SDK_INT; + if (firstSdkInt < Build.VERSION_CODES.BASE) { + Slog.e(TAG, "First SDK version " + firstSdkInt + " is invalid; must be " + + "at least VERSION_CODES.BASE"); + } File baseDir; if (firstSdkInt <= Build.VERSION_CODES.O_MR1) { baseDir = Environment.getUserSystemDirectory(userId); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 1078f6eefa76..36b04fcd6108 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -2537,6 +2537,7 @@ public class LockSettingsService extends ILockSettings.Stub { * Returns a fixed pseudorandom byte string derived from the user's synthetic password. * This is used to salt the password history hash to protect the hash against offline * bruteforcing, since rederiving this value requires a successful authentication. + * If user is a managed profile with unified challenge, currentCredential is ignored. */ @Override public byte[] getHashFactor(String currentCredential, int userId) throws RemoteException { @@ -2544,6 +2545,14 @@ public class LockSettingsService extends ILockSettings.Stub { if (TextUtils.isEmpty(currentCredential)) { currentCredential = null; } + if (isManagedProfileWithUnifiedLock(userId)) { + try { + currentCredential = getDecryptedPasswordForTiedProfile(userId); + } catch (Exception e) { + Slog.e(TAG, "Failed to get work profile credential", e); + return null; + } + } synchronized (mSpManager) { if (!isSyntheticPasswordBasedCredentialLocked(userId)) { Slog.w(TAG, "Synthetic password not enabled"); diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index b3aa0bf255e1..442354bbb6b9 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -235,6 +235,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { * @param packageName The package that made the original volume request. * @param pid The pid that made the original volume request. * @param uid The uid that made the original volume request. + * @param caller caller binder. can be {@code null} if it's from the volume key. * @param asSystemService {@code true} if the event sent to the session as if it was come from * the system service instead of the app process. This helps sessions to distinguish * between the key injection by the app and key events from the hardware devices. @@ -244,8 +245,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { * @param flags Any of the flags from {@link AudioManager}. * @param useSuggested True to use adjustSuggestedStreamVolume instead of */ - public void adjustVolume(String packageName, int pid, int uid, boolean asSystemService, - int direction, int flags, boolean useSuggested) { + public void adjustVolume(String packageName, int pid, int uid, + ISessionControllerCallback caller, boolean asSystemService, int direction, int flags, + boolean useSuggested) { int previousFlagPlaySound = flags & AudioManager.FLAG_PLAY_SOUND; if (isPlaybackActive() || hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) { flags &= ~AudioManager.FLAG_PLAY_SOUND; @@ -266,7 +268,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { Log.w(TAG, "Muting remote playback is not supported"); return; } - mSessionCb.adjustVolume(packageName, pid, uid, asSystemService, direction); + mSessionCb.adjustVolume(packageName, pid, uid, caller, asSystemService, direction); int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume); mOptimisticVolume = volumeBefore + direction; @@ -285,7 +287,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } } - public void setVolumeTo(String packageName, int pid, int uid, int value, int flags) { + private void setVolumeTo(String packageName, int pid, int uid, + ISessionControllerCallback caller, int value, int flags) { if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) { int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs); mAudioManagerInternal.setStreamVolumeForUid(stream, value, flags, packageName, uid); @@ -295,7 +298,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { return; } value = Math.max(0, Math.min(value, mMaxVolume)); - mSessionCb.setVolumeTo(packageName, pid, uid, value); + mSessionCb.setVolumeTo(packageName, pid, uid, caller, value); int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume); mOptimisticVolume = Math.max(0, Math.min(value, mMaxVolume)); @@ -611,6 +614,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { try { holder.mCallback.onVolumeInfoChanged(info); } catch (DeadObjectException e) { + mControllerCallbackHolders.remove(i); logCallbackException("Removing dead callback in pushVolumeUpdate", holder, e); } catch (RemoteException e) { logCallbackException("Unexpected exception in pushVolumeUpdate", holder, e); @@ -629,8 +633,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { try { holder.mCallback.onEvent(event, data); } catch (DeadObjectException e) { - logCallbackException("Removing dead callback in pushEvent", holder, e); mControllerCallbackHolders.remove(i); + logCallbackException("Removing dead callback in pushEvent", holder, e); } catch (RemoteException e) { logCallbackException("unexpected exception in pushEvent", holder, e); } @@ -705,14 +709,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { return -1; } - private String getPackageName(int uid) { - String[] packages = mContext.getPackageManager().getPackagesForUid(uid); - if (packages != null && packages.length > 0) { - return packages[0]; - } - return null; - } - private final Runnable mClearOptimisticVolumeRunnable = new Runnable() { @Override public void run() { @@ -913,14 +909,31 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, KeyEvent keyEvent, int sequenceId, ResultReceiver cb) { - Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); - mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); try { if (asSystemService) { mCb.onMediaButton(mContext.getPackageName(), Process.myPid(), - Process.SYSTEM_UID, mediaButtonIntent, sequenceId, cb); + Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), sequenceId, cb); + } else { + mCb.onMediaButton(packageName, pid, uid, + createMediaButtonIntent(keyEvent), sequenceId, cb); + } + return true; + } catch (RemoteException e) { + Slog.e(TAG, "Remote failure in sendMediaRequest.", e); + } + return false; + } + + public boolean sendMediaButton(String packageName, int pid, int uid, + ISessionControllerCallback caller, boolean asSystemService, + KeyEvent keyEvent) { + try { + if (asSystemService) { + mCb.onMediaButton(mContext.getPackageName(), Process.myPid(), + Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), 0, null); } else { - mCb.onMediaButton(packageName, pid, uid, mediaButtonIntent, sequenceId, cb); + mCb.onMediaButtonFromController(packageName, pid, uid, caller, + createMediaButtonIntent(keyEvent)); } return true; } catch (RemoteException e) { @@ -929,205 +942,221 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { return false; } - public void sendCommand(String packageName, int pid, int uid, String command, Bundle args, - ResultReceiver cb) { + public void sendCommand(String packageName, int pid, int uid, + ISessionControllerCallback caller, String command, Bundle args, ResultReceiver cb) { try { - mCb.onCommand(packageName, pid, uid, command, args, cb); + mCb.onCommand(packageName, pid, uid, caller, command, args, cb); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in sendCommand.", e); } } - public void sendCustomAction(String packageName, int pid, int uid, String action, + public void sendCustomAction(String packageName, int pid, int uid, + ISessionControllerCallback caller, String action, Bundle args) { try { - mCb.onCustomAction(packageName, pid, uid, action, args); + mCb.onCustomAction(packageName, pid, uid, caller, action, args); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in sendCustomAction.", e); } } - public void prepare(String packageName, int pid, int uid) { + public void prepare(String packageName, int pid, int uid, + ISessionControllerCallback caller) { try { - mCb.onPrepare(packageName, pid, uid); + mCb.onPrepare(packageName, pid, uid, caller); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in prepare.", e); } } - public void prepareFromMediaId(String packageName, int pid, int uid, String mediaId, - Bundle extras) { + public void prepareFromMediaId(String packageName, int pid, int uid, + ISessionControllerCallback caller, String mediaId, Bundle extras) { try { - mCb.onPrepareFromMediaId(packageName, pid, uid, mediaId, extras); + mCb.onPrepareFromMediaId(packageName, pid, uid, caller, mediaId, extras); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in prepareFromMediaId.", e); } } - public void prepareFromSearch(String packageName, int pid, int uid, String query, - Bundle extras) { + public void prepareFromSearch(String packageName, int pid, int uid, + ISessionControllerCallback caller, String query, Bundle extras) { try { - mCb.onPrepareFromSearch(packageName, pid, uid, query, extras); + mCb.onPrepareFromSearch(packageName, pid, uid, caller, query, extras); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in prepareFromSearch.", e); } } - public void prepareFromUri(String packageName, int pid, int uid, Uri uri, - Bundle extras) { + public void prepareFromUri(String packageName, int pid, int uid, + ISessionControllerCallback caller, Uri uri, Bundle extras) { try { - mCb.onPrepareFromUri(packageName, pid, uid, uri, extras); + mCb.onPrepareFromUri(packageName, pid, uid, caller, uri, extras); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in prepareFromUri.", e); } } - public void play(String packageName, int pid, int uid) { + public void play(String packageName, int pid, int uid, ISessionControllerCallback caller) { try { - mCb.onPlay(packageName, pid, uid); + mCb.onPlay(packageName, pid, uid, caller); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in play.", e); } } - public void playFromMediaId(String packageName, int pid, int uid, String mediaId, - Bundle extras) { + public void playFromMediaId(String packageName, int pid, int uid, + ISessionControllerCallback caller, String mediaId, Bundle extras) { try { - mCb.onPlayFromMediaId(packageName, pid, uid, mediaId, extras); + mCb.onPlayFromMediaId(packageName, pid, uid, caller, mediaId, extras); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in playFromMediaId.", e); } } - public void playFromSearch(String packageName, int pid, int uid, String query, - Bundle extras) { + public void playFromSearch(String packageName, int pid, int uid, + ISessionControllerCallback caller, String query, Bundle extras) { try { - mCb.onPlayFromSearch(packageName, pid, uid, query, extras); + mCb.onPlayFromSearch(packageName, pid, uid, caller, query, extras); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in playFromSearch.", e); } } - public void playFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) { + public void playFromUri(String packageName, int pid, int uid, + ISessionControllerCallback caller, Uri uri, Bundle extras) { try { - mCb.onPlayFromUri(packageName, pid, uid, uri, extras); + mCb.onPlayFromUri(packageName, pid, uid, caller, uri, extras); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in playFromUri.", e); } } - public void skipToTrack(String packageName, int pid, int uid, long id) { + public void skipToTrack(String packageName, int pid, int uid, + ISessionControllerCallback caller, long id) { try { - mCb.onSkipToTrack(packageName, pid, uid, id); + mCb.onSkipToTrack(packageName, pid, uid, caller, id); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in skipToTrack", e); } } - public void pause(String packageName, int pid, int uid) { + public void pause(String packageName, int pid, int uid, ISessionControllerCallback caller) { try { - mCb.onPause(packageName, pid, uid); + mCb.onPause(packageName, pid, uid, caller); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in pause.", e); } } - public void stop(String packageName, int pid, int uid) { + public void stop(String packageName, int pid, int uid, ISessionControllerCallback caller) { try { - mCb.onStop(packageName, pid, uid); + mCb.onStop(packageName, pid, uid, caller); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in stop.", e); } } - public void next(String packageName, int pid, int uid) { + public void next(String packageName, int pid, int uid, ISessionControllerCallback caller) { try { - mCb.onNext(packageName, pid, uid); + mCb.onNext(packageName, pid, uid, caller); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in next.", e); } } - public void previous(String packageName, int pid, int uid) { + public void previous(String packageName, int pid, int uid, + ISessionControllerCallback caller) { try { - mCb.onPrevious(packageName, pid, uid); + mCb.onPrevious(packageName, pid, uid, caller); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in previous.", e); } } - public void fastForward(String packageName, int pid, int uid) { + public void fastForward(String packageName, int pid, int uid, + ISessionControllerCallback caller) { try { - mCb.onFastForward(packageName, pid, uid); + mCb.onFastForward(packageName, pid, uid, caller); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in fastForward.", e); } } - public void rewind(String packageName, int pid, int uid) { + public void rewind(String packageName, int pid, int uid, + ISessionControllerCallback caller) { try { - mCb.onRewind(packageName, pid, uid); + mCb.onRewind(packageName, pid, uid, caller); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in rewind.", e); } } - public void seekTo(String packageName, int pid, int uid, long pos) { + public void seekTo(String packageName, int pid, int uid, ISessionControllerCallback caller, + long pos) { try { - mCb.onSeekTo(packageName, pid, uid, pos); + mCb.onSeekTo(packageName, pid, uid, caller, pos); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in seekTo.", e); } } - public void rate(String packageName, int pid, int uid, Rating rating) { + public void rate(String packageName, int pid, int uid, ISessionControllerCallback caller, + Rating rating) { try { - mCb.onRate(packageName, pid, uid, rating); + mCb.onRate(packageName, pid, uid, caller, rating); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in rate.", e); } } - public void adjustVolume(String packageName, int pid, int uid, boolean asSystemService, - int direction) { + public void adjustVolume(String packageName, int pid, int uid, + ISessionControllerCallback caller, boolean asSystemService, int direction) { try { if (asSystemService) { mCb.onAdjustVolume(mContext.getPackageName(), Process.myPid(), - Process.SYSTEM_UID, direction); + Process.SYSTEM_UID, null, direction); } else { - mCb.onAdjustVolume(packageName, pid, uid, direction); + mCb.onAdjustVolume(packageName, pid, uid, caller, direction); } } catch (RemoteException e) { Slog.e(TAG, "Remote failure in adjustVolume.", e); } } - public void setVolumeTo(String packageName, int pid, int uid, int value) { + public void setVolumeTo(String packageName, int pid, int uid, + ISessionControllerCallback caller, int value) { try { - mCb.onSetVolumeTo(packageName, pid, uid, value); + mCb.onSetVolumeTo(packageName, pid, uid, caller, value); } catch (RemoteException e) { Slog.e(TAG, "Remote failure in setVolumeTo.", e); } } + + private Intent createMediaButtonIntent(KeyEvent keyEvent) { + Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); + mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); + return mediaButtonIntent; + } } class ControllerStub extends ISessionController.Stub { @Override - public void sendCommand(String packageName, String command, Bundle args, - ResultReceiver cb) { + public void sendCommand(String packageName, ISessionControllerCallback caller, + String command, Bundle args, ResultReceiver cb) { mSessionCb.sendCommand(packageName, Binder.getCallingPid(), Binder.getCallingUid(), - command, args, cb); + caller, command, args, cb); } @Override - public boolean sendMediaButton(String packageName, boolean asSystemService, - KeyEvent mediaButtonIntent) { + public boolean sendMediaButton(String packageName, ISessionControllerCallback cb, + boolean asSystemService, KeyEvent keyEvent) { return mSessionCb.sendMediaButton(packageName, Binder.getCallingPid(), - Binder.getCallingUid(), asSystemService, mediaButtonIntent, 0, null); + Binder.getCallingUid(), cb, asSystemService, keyEvent); } @Override - public void registerCallbackListener(ISessionControllerCallback cb) { + public void registerCallbackListener(String packageName, ISessionControllerCallback cb) { synchronized (mLock) { // If this session is already destroyed tell the caller and // don't add them. @@ -1141,24 +1170,24 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } if (getControllerHolderIndexForCb(cb) < 0) { mControllerCallbackHolders.add(new ISessionControllerCallbackHolder(cb, - Binder.getCallingUid())); + packageName, Binder.getCallingUid())); if (DEBUG) { - Log.d(TAG, "registering controller callback " + cb); + Log.d(TAG, "registering controller callback " + cb + " from controller" + + packageName); } } } } @Override - public void unregisterCallbackListener(ISessionControllerCallback cb) - throws RemoteException { + public void unregisterCallbackListener(ISessionControllerCallback cb) { synchronized (mLock) { int index = getControllerHolderIndexForCb(cb); if (index != -1) { mControllerCallbackHolders.remove(index); } if (DEBUG) { - Log.d(TAG, "unregistering callback " + cb + ". index=" + index); + Log.d(TAG, "unregistering callback " + cb.asBinder()); } } } @@ -1204,13 +1233,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public void adjustVolume(String packageName, boolean asSystemService, int direction, - int flags) { + public void adjustVolume(String packageName, ISessionControllerCallback caller, + boolean asSystemService, int direction, int flags) { int pid = Binder.getCallingPid(); int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { - MediaSessionRecord.this.adjustVolume(packageName, pid, uid, asSystemService, + MediaSessionRecord.this.adjustVolume(packageName, pid, uid, caller, asSystemService, direction, flags, false /* useSuggested */); } finally { Binder.restoreCallingIdentity(token); @@ -1218,115 +1247,128 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public void setVolumeTo(String packageName, int value, int flags) { + public void setVolumeTo(String packageName, ISessionControllerCallback caller, + int value, int flags) { int pid = Binder.getCallingPid(); int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { - MediaSessionRecord.this.setVolumeTo(packageName, pid, uid, value, flags); + MediaSessionRecord.this.setVolumeTo(packageName, pid, uid, caller, value, flags); } finally { Binder.restoreCallingIdentity(token); } } @Override - public void prepare(String packageName) { - mSessionCb.prepare(packageName, Binder.getCallingPid(), Binder.getCallingUid()); + public void prepare(String packageName, ISessionControllerCallback caller) { + mSessionCb.prepare(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller); } @Override - public void prepareFromMediaId(String packageName, String mediaId, Bundle extras) { + public void prepareFromMediaId(String packageName, ISessionControllerCallback caller, + String mediaId, Bundle extras) { mSessionCb.prepareFromMediaId(packageName, Binder.getCallingPid(), - Binder.getCallingUid(), mediaId, extras); + Binder.getCallingUid(), caller, mediaId, extras); } @Override - public void prepareFromSearch(String packageName, String query, Bundle extras) { + public void prepareFromSearch(String packageName, ISessionControllerCallback caller, + String query, Bundle extras) { mSessionCb.prepareFromSearch(packageName, Binder.getCallingPid(), - Binder.getCallingUid(), query, extras); + Binder.getCallingUid(), caller, query, extras); } @Override - public void prepareFromUri(String packageName, Uri uri, Bundle extras) { + public void prepareFromUri(String packageName, ISessionControllerCallback caller, + Uri uri, Bundle extras) { mSessionCb.prepareFromUri(packageName, Binder.getCallingPid(), Binder.getCallingUid(), - uri, extras); + caller, uri, extras); } @Override - public void play(String packageName) { - mSessionCb.play(packageName, Binder.getCallingPid(), Binder.getCallingUid()); + public void play(String packageName, ISessionControllerCallback caller) { + mSessionCb.play(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller); } @Override - public void playFromMediaId(String packageName, String mediaId, Bundle extras) { + public void playFromMediaId(String packageName, ISessionControllerCallback caller, + String mediaId, Bundle extras) { mSessionCb.playFromMediaId(packageName, Binder.getCallingPid(), Binder.getCallingUid(), - mediaId, extras); + caller, mediaId, extras); } @Override - public void playFromSearch(String packageName, String query, Bundle extras) { + public void playFromSearch(String packageName, ISessionControllerCallback caller, + String query, Bundle extras) { mSessionCb.playFromSearch(packageName, Binder.getCallingPid(), Binder.getCallingUid(), - query, extras); + caller, query, extras); } @Override - public void playFromUri(String packageName, Uri uri, Bundle extras) { + public void playFromUri(String packageName, ISessionControllerCallback caller, + Uri uri, Bundle extras) { mSessionCb.playFromUri(packageName, Binder.getCallingPid(), Binder.getCallingUid(), - uri, extras); + caller, uri, extras); } @Override - public void skipToQueueItem(String packageName, long id) { - mSessionCb.skipToTrack(packageName, Binder.getCallingPid(), Binder.getCallingUid(), id); + public void skipToQueueItem(String packageName, ISessionControllerCallback caller, + long id) { + mSessionCb.skipToTrack(packageName, Binder.getCallingPid(), Binder.getCallingUid(), + caller, id); } @Override - public void pause(String packageName) { - mSessionCb.pause(packageName, Binder.getCallingPid(), Binder.getCallingUid()); + public void pause(String packageName, ISessionControllerCallback caller) { + mSessionCb.pause(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller); } @Override - public void stop(String packageName) { - mSessionCb.stop(packageName, Binder.getCallingPid(), Binder.getCallingUid()); + public void stop(String packageName, ISessionControllerCallback caller) { + mSessionCb.stop(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller); } @Override - public void next(String packageName) { - mSessionCb.next(packageName, Binder.getCallingPid(), Binder.getCallingUid()); + public void next(String packageName, ISessionControllerCallback caller) { + mSessionCb.next(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller); } @Override - public void previous(String packageName) { - mSessionCb.previous(packageName, Binder.getCallingPid(), Binder.getCallingUid()); + public void previous(String packageName, ISessionControllerCallback caller) { + mSessionCb.previous(packageName, Binder.getCallingPid(), Binder.getCallingUid(), + caller); } @Override - public void fastForward(String packageName) { - mSessionCb.fastForward(packageName, Binder.getCallingPid(), Binder.getCallingUid()); + public void fastForward(String packageName, ISessionControllerCallback caller) { + mSessionCb.fastForward(packageName, Binder.getCallingPid(), Binder.getCallingUid(), + caller); } @Override - public void rewind(String packageName) { - mSessionCb.rewind(packageName, Binder.getCallingPid(), Binder.getCallingUid()); + public void rewind(String packageName, ISessionControllerCallback caller) { + mSessionCb.rewind(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller); } @Override - public void seekTo(String packageName, long pos) { - mSessionCb.seekTo(packageName, Binder.getCallingPid(), Binder.getCallingUid(), pos); + public void seekTo(String packageName, ISessionControllerCallback caller, long pos) { + mSessionCb.seekTo(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller, + pos); } @Override - public void rate(String packageName, Rating rating) { - mSessionCb.rate(packageName, Binder.getCallingPid(), Binder.getCallingUid(), rating); + public void rate(String packageName, ISessionControllerCallback caller, Rating rating) { + mSessionCb.rate(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller, + rating); } @Override - public void sendCustomAction(String packageName, String action, Bundle args) { + public void sendCustomAction(String packageName, ISessionControllerCallback caller, + String action, Bundle args) { mSessionCb.sendCustomAction(packageName, Binder.getCallingPid(), Binder.getCallingUid(), - action, args); + caller, action, args); } - @Override public MediaMetadata getMetadata() { synchronized (mLock) { @@ -1372,10 +1414,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private class ISessionControllerCallbackHolder { private final ISessionControllerCallback mCallback; private final String mPackageName; + private final int mUid; - ISessionControllerCallbackHolder(ISessionControllerCallback callback, int uid) { + ISessionControllerCallbackHolder(ISessionControllerCallback callback, String packageName, + int uid) { mCallback = callback; - mPackageName = getPackageName(uid); + mPackageName = packageName; + mUid = uid; } } diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index a6e9389b3549..6fff3676ac8d 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -1851,7 +1851,7 @@ public class MediaSessionService extends SystemService implements Monitor { } }); } else { - session.adjustVolume(packageName, pid, uid, asSystemService, + session.adjustVolume(packageName, pid, uid, null, asSystemService, direction, flags, true); } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 5bd7c0b9ba9c..ab482bb2da98 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -2706,6 +2706,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private void normalizePoliciesNL(NetworkPolicy[] policies) { mNetworkPolicy.clear(); for (NetworkPolicy policy : policies) { + if (policy == null) { + continue; + } // When two normalized templates conflict, prefer the most // restrictive policy policy.template = NetworkTemplate.normalize(policy.template, mMergedSubscriberIds); diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java index 4bee55ef8b22..a16dcf358d59 100644 --- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java +++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java @@ -241,6 +241,20 @@ public class NetworkStatsRecorder { NetworkStats.Entry entry = null; for (int i = 0; i < delta.size(); i++) { entry = delta.getValues(i, entry); + + // As a last-ditch sanity check, report any negative values and + // clamp them so recording below doesn't croak. + if (entry.isNegative()) { + if (mObserver != null) { + mObserver.foundNonMonotonic(delta, i, mCookie); + } + entry.rxBytes = Math.max(entry.rxBytes, 0); + entry.rxPackets = Math.max(entry.rxPackets, 0); + entry.txBytes = Math.max(entry.txBytes, 0); + entry.txPackets = Math.max(entry.txPackets, 0); + entry.operations = Math.max(entry.operations, 0); + } + final NetworkIdentitySet ident = ifaceIdent.get(entry.iface); if (ident == null) { unknownIfaces.add(entry.iface); diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 492c6d6d51c7..addc479404e9 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -1716,7 +1716,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public void foundNonMonotonic(NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, String cookie) { - Log.w(TAG, "found non-monotonic values; saving to dropbox"); + Log.w(TAG, "Found non-monotonic values; saving to dropbox"); // record error for debugging final StringBuilder builder = new StringBuilder(); @@ -1725,9 +1725,21 @@ public class NetworkStatsService extends INetworkStatsService.Stub { builder.append("left=").append(left).append('\n'); builder.append("right=").append(right).append('\n'); - final DropBoxManager dropBox = (DropBoxManager) mContext.getSystemService( - Context.DROPBOX_SERVICE); - dropBox.addText(TAG_NETSTATS_ERROR, builder.toString()); + mContext.getSystemService(DropBoxManager.class).addText(TAG_NETSTATS_ERROR, + builder.toString()); + } + + @Override + public void foundNonMonotonic( + NetworkStats stats, int statsIndex, String cookie) { + Log.w(TAG, "Found non-monotonic values; saving to dropbox"); + + final StringBuilder builder = new StringBuilder(); + builder.append("Found non-monotonic " + cookie + " values at [" + statsIndex + "]\n"); + builder.append("stats=").append(stats).append('\n'); + + mContext.getSystemService(DropBoxManager.class).addText(TAG_NETSTATS_ERROR, + builder.toString()); } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index e2e6f6dbad79..8f7fa1b2a7f7 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -821,6 +821,7 @@ public class NotificationManagerService extends SystemService { } } r.setVisibility(true, nv.rank, nv.count); + maybeRecordInterruptionLocked(r); nv.recycle(); } // Note that we might receive this event after notifications @@ -1928,11 +1929,12 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") protected void maybeRecordInterruptionLocked(NotificationRecord r) { - if (r.isInterruptive()) { + if (r.isInterruptive() && !r.hasRecordedInterruption()) { mAppUsageStats.reportInterruptiveNotification(r.sbn.getPackageName(), r.getChannel().getId(), getRealUserId(r.sbn.getUserId())); logRecentLocked(r); + r.setRecordedInterruption(true); } } @@ -2669,6 +2671,7 @@ public class NotificationManagerService extends SystemService { if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]); reportSeen(r); r.setSeen(); + maybeRecordInterruptionLocked(r); } } } @@ -4440,7 +4443,7 @@ public class NotificationManagerService extends SystemService { if (index < 0) { mNotificationList.add(r); mUsageStats.registerPostedByApp(r); - r.setInterruptive(isVisuallyInterruptive(null, r)); + r.setInterruptive(true); } else { old = mNotificationList.get(index); mNotificationList.set(index, r); @@ -4449,7 +4452,7 @@ public class NotificationManagerService extends SystemService { notification.flags |= old.getNotification().flags & FLAG_FOREGROUND_SERVICE; r.isUpdate = true; - r.setInterruptive(isVisuallyInterruptive(old, r)); + r.setTextChanged(isVisuallyInterruptive(old, r)); } mNotificationsByKey.put(n.getKey(), r); diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 2aec3eaf757c..0f3f44eae2d3 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -158,6 +158,8 @@ public final class NotificationRecord { private final NotificationStats mStats; private int mUserSentiment; private boolean mIsInterruptive; + private boolean mTextChanged; + private boolean mRecordedInterruption; private int mNumberOfSmartRepliesAdded; private boolean mHasSeenSmartReplies; /** @@ -839,6 +841,9 @@ public final class NotificationRecord { /** Mark the notification as seen by the user. */ public void setSeen() { mStats.setSeen(); + if (mTextChanged) { + mIsInterruptive = true; + } } public void setAuthoritativeRank(int authoritativeRank) { @@ -935,6 +940,18 @@ public final class NotificationRecord { mIsInterruptive = interruptive; } + public void setTextChanged(boolean textChanged) { + mTextChanged = textChanged; + } + + public void setRecordedInterruption(boolean recorded) { + mRecordedInterruption = recorded; + } + + public boolean hasRecordedInterruption() { + return mRecordedInterruption; + } + public boolean isInterruptive() { return mIsInterruptive; } diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index 7f141eeba839..61b5415ec7a3 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -25,22 +25,18 @@ import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.content.Context; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; -import android.content.pm.Signature; import android.metrics.LogMaker; import android.os.Build; import android.os.UserHandle; -import android.print.PrintManager; import android.provider.Settings.Secure; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.RankingHelperProto; import android.service.notification.RankingHelperProto.RecordProto; import android.text.TextUtils; import android.util.ArrayMap; -import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; import android.util.proto.ProtoOutputStream; @@ -156,6 +152,8 @@ public class RankingHelper implements RankingConfig { } } + mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state & + NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1; updateChannelsBypassingDnd(); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index f4853f3b2056..5f9a363dc728 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2072,6 +2072,16 @@ public class PackageManagerService extends IPackageManager.Stub installerPackageName, null /*finishedReceiver*/, updateUserIds, instantUserIds); } + // if the required verifier is defined, but, is not the installer of record + // for the package, it gets notified + final boolean notifyVerifier = mRequiredVerifierPackage != null + && !mRequiredVerifierPackage.equals(installerPackageName); + if (notifyVerifier) { + sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, + extras, 0 /*flags*/, + mRequiredVerifierPackage, null /*finishedReceiver*/, + updateUserIds, instantUserIds); + } // Send replaced for users that don't see the package for the first time if (update) { @@ -2085,6 +2095,12 @@ public class PackageManagerService extends IPackageManager.Stub installerPackageName, null /*finishedReceiver*/, updateUserIds, instantUserIds); } + if (notifyVerifier) { + sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, + extras, 0 /*flags*/, + mRequiredVerifierPackage, null /*finishedReceiver*/, + updateUserIds, instantUserIds); + } sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null /*package*/, null /*extras*/, 0 /*flags*/, packageName /*targetPackage*/, @@ -24384,6 +24400,9 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); if (info.targetSdkVersion < Build.VERSION_CODES.O) { return false; } + if (isInstantApp(packageName, userId)) { + return false; + } String appOpPermission = Manifest.permission.REQUEST_INSTALL_PACKAGES; String[] packagesDeclaringPermission = getAppOpPermissionPackages(appOpPermission); if (!ArrayUtils.contains(packagesDeclaringPermission, packageName)) { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index dd0a5d24cc01..06c56a05d42a 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -873,9 +873,8 @@ public class UserManagerService extends IUserManager.Stub { throw new SecurityException("MANAGE_USERS permission is required to start intent " + "after disabling quiet mode."); } - final boolean hasModifyQuietModePermission = ActivityManager.checkComponentPermission( - Manifest.permission.MODIFY_QUIET_MODE, - callingUid, -1, true) == PackageManager.PERMISSION_GRANTED; + final boolean hasModifyQuietModePermission = hasPermissionGranted( + Manifest.permission.MODIFY_QUIET_MODE, callingUid); if (hasModifyQuietModePermission) { return; } @@ -1002,6 +1001,30 @@ public class UserManagerService extends IUserManager.Stub { } } + @Override + public void setUserAdmin(int userId) { + checkManageUserAndAcrossUsersFullPermission("set user admin"); + + synchronized (mPackagesLock) { + UserInfo info; + synchronized (mUsersLock) { + info = getUserInfoLU(userId); + } + if (info == null || info.isAdmin()) { + // Exit if no user found with that id, or the user is already an Admin. + return; + } + + info.flags ^= UserInfo.FLAG_ADMIN; + writeUserLP(getUserDataLU(info.id)); + } + + // Remove non-admin restrictions. + // Keep synchronized with createUserEvenWhenDisallowed. + setUserRestriction(UserManager.DISALLOW_SMS, false, userId); + setUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, false, userId); + } + /** * Evicts a user's CE key by stopping and restarting the user. * @@ -1122,8 +1145,8 @@ public class UserManagerService extends IUserManager.Stub { hasManageUsersPermission()) { return; } - if (ActivityManager.checkComponentPermission(Manifest.permission.INTERACT_ACROSS_USERS, - Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) { + if (!hasPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS, + Binder.getCallingUid())) { throw new SecurityException("You need INTERACT_ACROSS_USERS or MANAGE_USERS permission " + "to: check " + name); } @@ -1801,17 +1824,26 @@ public class UserManagerService extends IUserManager.Stub { */ private static final void checkManageUserAndAcrossUsersFullPermission(String message) { final int uid = Binder.getCallingUid(); - if (uid != Process.SYSTEM_UID && uid != 0 - && ActivityManager.checkComponentPermission( - Manifest.permission.MANAGE_USERS, - uid, -1, true) != PackageManager.PERMISSION_GRANTED - && ActivityManager.checkComponentPermission( - Manifest.permission.INTERACT_ACROSS_USERS_FULL, - uid, -1, true) != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException( - "You need MANAGE_USERS and INTERACT_ACROSS_USERS_FULL permission to: " - + message); + + if (uid == Process.SYSTEM_UID || uid == 0) { + // System UID or root's UID are granted privilege. + return; } + + if (hasPermissionGranted(Manifest.permission.MANAGE_USERS, uid) + && hasPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid)) { + // Apps with both permissions are granted privilege. + return; + } + + throw new SecurityException( + "You need MANAGE_USERS and INTERACT_ACROSS_USERS_FULL permission to: " + message); + } + + private static boolean hasPermissionGranted(String permission, int uid) { + return ActivityManager.checkComponentPermission( + permission, uid, /* owningUid = */-1, /* exported = */ true) == + PackageManager.PERMISSION_GRANTED; } /** @@ -1872,9 +1904,7 @@ public class UserManagerService extends IUserManager.Stub { final int callingUid = Binder.getCallingUid(); return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) || callingUid == Process.ROOT_UID - || ActivityManager.checkComponentPermission( - android.Manifest.permission.MANAGE_USERS, - callingUid, -1, true) == PackageManager.PERMISSION_GRANTED; + || hasPermissionGranted(android.Manifest.permission.MANAGE_USERS, callingUid); } /** @@ -1886,12 +1916,8 @@ public class UserManagerService extends IUserManager.Stub { final int callingUid = Binder.getCallingUid(); return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) || callingUid == Process.ROOT_UID - || ActivityManager.checkComponentPermission( - android.Manifest.permission.MANAGE_USERS, - callingUid, -1, true) == PackageManager.PERMISSION_GRANTED - || ActivityManager.checkComponentPermission( - android.Manifest.permission.CREATE_USERS, - callingUid, -1, true) == PackageManager.PERMISSION_GRANTED; + || hasPermissionGranted(android.Manifest.permission.MANAGE_USERS, callingUid) + || hasPermissionGranted(android.Manifest.permission.CREATE_USERS, callingUid); } /** diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index d9117613f6fe..61400da6e452 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -234,7 +234,6 @@ import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.DisplayCutout; -import android.view.DisplayInfo; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.IApplicationToken; @@ -2292,6 +2291,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { public void onTrustedChanged() { mWindowManagerFuncs.notifyKeyguardTrustedChanged(); } + + @Override + public void onShowingChanged() { + mWindowManagerFuncs.onKeyguardShowingAndNotOccludedChanged(); + } }); mScreenshotHelper = new ScreenshotHelper(mContext); } diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 33eafb9e3e33..02dd6e61707c 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -660,6 +660,11 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * abort animations that have no timeout, in case they got stuck. */ void triggerAnimationFailsafe(); + + /** + * The keyguard showing state has changed + */ + void onKeyguardShowingAndNotOccludedChanged(); } /** Window has been added to the screen. */ diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java index e56caf849aac..1cba1c7bed1b 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java @@ -94,6 +94,7 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { public void onShowingStateChanged(boolean showing) { mIsShowing = showing; + mCallback.onShowingChanged(); try { mKeystoreService.onKeyguardVisibilityChanged(showing, mCurrentUserId); } catch (RemoteException e) { @@ -132,6 +133,7 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { public interface StateCallback { void onTrustedChanged(); + void onShowingChanged(); } public void dump(String prefix, PrintWriter pw) { diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java index 2621c50fb2f2..c3b984172802 100644 --- a/services/core/java/com/android/server/slice/SliceManagerService.java +++ b/services/core/java/com/android/server/slice/SliceManagerService.java @@ -55,6 +55,7 @@ import android.os.ShellCallback; import android.os.UserHandle; import android.util.ArrayMap; import android.util.Slog; +import android.util.SparseArray; import android.util.Xml.Encoding; import com.android.internal.annotations.GuardedBy; @@ -77,6 +78,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.function.Supplier; public class SliceManagerService extends ISliceManager.Stub { @@ -90,6 +92,10 @@ public class SliceManagerService extends ISliceManager.Stub { @GuardedBy("mLock") private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>(); + @GuardedBy("mLock") + private final SparseArray<PackageMatchingCache> mAssistantLookup = new SparseArray<>(); + @GuardedBy("mLock") + private final SparseArray<PackageMatchingCache> mHomeLookup = new SparseArray<>(); private final Handler mHandler; private final SlicePermissionManager mPermissions; @@ -453,17 +459,37 @@ public class SliceManagerService extends ISliceManager.Stub { } private boolean isAssistant(String pkg, int userId) { - final ComponentName cn = mAssistUtils.getAssistComponentForUser(userId); - if (cn == null) { - return false; - } - return cn.getPackageName().equals(pkg); + return getAssistantMatcher(userId).matches(pkg); } private boolean isDefaultHomeApp(String pkg, int userId) { - String defaultHome = getDefaultHome(userId); + return getHomeMatcher(userId).matches(pkg); + } - return pkg != null && Objects.equals(pkg, defaultHome); + private PackageMatchingCache getAssistantMatcher(int userId) { + PackageMatchingCache matcher = mAssistantLookup.get(userId); + if (matcher == null) { + matcher = new PackageMatchingCache(() -> getAssistant(userId)); + mAssistantLookup.put(userId, matcher); + } + return matcher; + } + + private PackageMatchingCache getHomeMatcher(int userId) { + PackageMatchingCache matcher = mHomeLookup.get(userId); + if (matcher == null) { + matcher = new PackageMatchingCache(() -> getDefaultHome(userId)); + mHomeLookup.put(userId, matcher); + } + return matcher; + } + + private String getAssistant(int userId) { + final ComponentName cn = mAssistUtils.getAssistComponentForUser(userId); + if (cn == null) { + return null; + } + return cn.getPackageName(); } // Based on getDefaultHome in ShortcutService. @@ -559,6 +585,30 @@ public class SliceManagerService extends ISliceManager.Stub { return mPermissions.getAllPackagesGranted(pkg); } + /** + * Holder that caches a package that has access to a slice. + */ + static class PackageMatchingCache { + + private final Supplier<String> mPkgSource; + private String mCurrentPkg; + + public PackageMatchingCache(Supplier<String> pkgSource) { + mPkgSource = pkgSource; + } + + public boolean matches(String pkgCandidate) { + if (pkgCandidate == null) return false; + + if (Objects.equals(pkgCandidate, mCurrentPkg)) { + return true; + } + // Failed on cached value, try updating. + mCurrentPkg = mPkgSource.get(); + return Objects.equals(pkgCandidate, mCurrentPkg); + } + } + public static class Lifecycle extends SystemService { private SliceManagerService mService; diff --git a/services/core/java/com/android/server/updates/ApnDbInstallReceiver.java b/services/core/java/com/android/server/updates/ApnDbInstallReceiver.java index 3b6f9d6c135c..8d53143a29a6 100644 --- a/services/core/java/com/android/server/updates/ApnDbInstallReceiver.java +++ b/services/core/java/com/android/server/updates/ApnDbInstallReceiver.java @@ -29,7 +29,7 @@ public class ApnDbInstallReceiver extends ConfigUpdateInstallReceiver { "update_db"); public ApnDbInstallReceiver() { - super("/data/misc/", "apns-conf.xml", "metadata/", "version"); + super("/data/misc/apns/", "apns-conf.xml", "metadata/", "version"); } @Override diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index a7c203dca03b..288612621f2c 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -51,6 +51,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.content.res.Resources; +import android.database.ContentObserver; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; @@ -75,6 +76,7 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; +import android.provider.Settings; import android.service.wallpaper.IWallpaperConnection; import android.service.wallpaper.IWallpaperEngine; import android.service.wallpaper.IWallpaperService; @@ -336,6 +338,102 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } + /** + * Observes changes of theme settings. It will check whether to call + * notifyWallpaperColorsChanged by the current theme and updated theme. + * The light theme and dark theme are controlled by the hint values in Wallpaper colors, + * threrfore, if light theme mode is chosen, HINT_SUPPORTS_DARK_THEME in hint will be + * removed and then notify listeners. + */ + private class ThemeSettingsObserver extends ContentObserver { + + public ThemeSettingsObserver(Handler handler) { + super(handler); + } + + public void startObserving(Context context) { + context.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.THEME_MODE), + false, + this); + } + + public void stopObserving(Context context) { + context.getContentResolver().unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange) { + onThemeSettingsChanged(); + } + } + + /** + * Check whether to call notifyWallpaperColorsChanged. Assumed that the theme mode + * was wallpaper theme mode and dark wallpaper was set, therefoe, the theme was dark. + * Then theme mode changing to dark theme mode, however, theme should not update since + * theme was dark already. + */ + private boolean needUpdateLocked(WallpaperColors colors, int themeMode) { + if (colors == null) { + return false; + } + + if (themeMode == mThemeMode) { + return false; + } + + boolean result = true; + boolean supportDarkTheme = + (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0; + switch (themeMode) { + case Settings.Secure.THEME_MODE_WALLPAPER: + if (mThemeMode == Settings.Secure.THEME_MODE_LIGHT) { + result = supportDarkTheme; + } else { + result = !supportDarkTheme; + } + break; + case Settings.Secure.THEME_MODE_LIGHT: + if (mThemeMode == Settings.Secure.THEME_MODE_WALLPAPER) { + result = supportDarkTheme; + } + break; + case Settings.Secure.THEME_MODE_DARK: + if (mThemeMode == Settings.Secure.THEME_MODE_WALLPAPER) { + result = !supportDarkTheme; + } + break; + default: + Slog.w(TAG, "unkonwn theme mode " + themeMode); + return false; + } + mThemeMode = themeMode; + return result; + } + + void onThemeSettingsChanged() { + WallpaperData wallpaper; + synchronized (mLock) { + wallpaper = mWallpaperMap.get(mCurrentUserId); + int updatedThemeMode = Settings.Secure.getInt( + mContext.getContentResolver(), Settings.Secure.THEME_MODE, + Settings.Secure.THEME_MODE_WALLPAPER); + + if (DEBUG) { + Slog.v(TAG, "onThemeSettingsChanged, mode = " + updatedThemeMode); + } + + if (!needUpdateLocked(wallpaper.primaryColors, updatedThemeMode)) { + return; + } + } + + if (wallpaper != null) { + notifyWallpaperColorsChanged(wallpaper, FLAG_SYSTEM); + } + } + void notifyLockWallpaperChanged() { final IWallpaperManagerCallback cb = mKeyguardListener; if (cb != null) { @@ -413,6 +511,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } userAllColorListeners.finishBroadcast(); } + wallpaperColors = getThemeColorsLocked(wallpaperColors); } final int count = colorListeners.size(); @@ -481,6 +580,40 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } /** + * We can easily change theme by modified colors hint. This function will check + * current theme mode and return the WallpaperColors fit current theme mode. + * If color need modified, it will return a copied WallpaperColors which + * its ColorsHint is modified to fit current theme mode. + * + * @param colors a wallpaper primary colors representation + */ + private WallpaperColors getThemeColorsLocked(WallpaperColors colors) { + if (colors == null) { + Slog.w(TAG, "Cannot get theme colors because WallpaperColors is null."); + return null; + } + + int colorHints = colors.getColorHints(); + boolean supportDarkTheme = (colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0; + if (mThemeMode == Settings.Secure.THEME_MODE_WALLPAPER || + (mThemeMode == Settings.Secure.THEME_MODE_LIGHT && !supportDarkTheme) || + (mThemeMode == Settings.Secure.THEME_MODE_DARK && supportDarkTheme)) { + return colors; + } + + WallpaperColors themeColors = new WallpaperColors(colors.getPrimaryColor(), + colors.getSecondaryColor(), colors.getTertiaryColor()); + + if (mThemeMode == Settings.Secure.THEME_MODE_LIGHT) { + colorHints &= ~WallpaperColors.HINT_SUPPORTS_DARK_THEME; + } else if (mThemeMode == Settings.Secure.THEME_MODE_DARK) { + colorHints |= WallpaperColors.HINT_SUPPORTS_DARK_THEME; + } + themeColors.setColorHints(colorHints); + return themeColors; + } + + /** * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped * for display. */ @@ -676,6 +809,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub final SparseArray<Boolean> mUserRestorecon = new SparseArray<Boolean>(); int mCurrentUserId = UserHandle.USER_NULL; boolean mInAmbientMode; + int mThemeMode; static class WallpaperData { @@ -734,6 +868,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub long lastDiedTime; boolean wallpaperUpdating; WallpaperObserver wallpaperObserver; + ThemeSettingsObserver themeSettingsObserver; /** * List of callbacks registered they should each be notified when the wallpaper is changed. @@ -1279,6 +1414,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub wallpaper.wallpaperObserver.stopWatching(); wallpaper.wallpaperObserver = null; } + if (wallpaper.themeSettingsObserver != null) { + wallpaper.themeSettingsObserver.stopObserving(mContext); + wallpaper.themeSettingsObserver = null; + } } } @@ -1362,6 +1501,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper); systemWallpaper.wallpaperObserver.startWatching(); } + if (systemWallpaper.themeSettingsObserver == null) { + systemWallpaper.themeSettingsObserver = new ThemeSettingsObserver(null); + systemWallpaper.themeSettingsObserver.startObserving(mContext); + } + mThemeMode = Settings.Secure.getInt( + mContext.getContentResolver(), Settings.Secure.THEME_MODE, + Settings.Secure.THEME_MODE_WALLPAPER); switchWallpaper(systemWallpaper, reply); } @@ -1835,7 +1981,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } synchronized (mLock) { - return wallpaperData.primaryColors; + return getThemeColorsLocked(wallpaperData.primaryColors); } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 50d0d0a5f1f9..a709c55bbd30 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -157,10 +157,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { } WindowState computeFocusedWindow() { + // While the keyguard is showing, we must focus anything besides the main display. + // Otherwise we risk input not going to the keyguard when the user expects it to. + final boolean forceDefaultDisplay = mService.isKeyguardShowingAndNotOccluded(); + for (int i = mChildren.size() - 1; i >= 0; i--) { final DisplayContent dc = mChildren.get(i); final WindowState win = dc.findFocusedWindow(); if (win != null) { + if (forceDefaultDisplay && !dc.isDefaultDisplay) { + EventLog.writeEvent(0x534e4554, "71786287", win.mOwnerUid, ""); + continue; + } return win; } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index cb641425f6bc..f42f855e7ac5 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2846,6 +2846,11 @@ public class WindowManagerService extends IWindowManager.Stub mH.sendEmptyMessage(H.ANIMATION_FAILSAFE); } + @Override + public void onKeyguardShowingAndNotOccludedChanged() { + mH.sendEmptyMessage(H.RECOMPUTE_FOCUS); + } + /** * Starts deferring layout passes. Useful when doing multiple changes but to optimize * performance, only one layout pass should be done. This can be called multiple times, and @@ -4626,6 +4631,7 @@ public class WindowManagerService extends IWindowManager.Stub public static final int SET_HAS_OVERLAY_UI = 58; public static final int SET_RUNNING_REMOTE_ANIMATION = 59; public static final int ANIMATION_FAILSAFE = 60; + public static final int RECOMPUTE_FOCUS = 61; /** * Used to denote that an integer field in a message will not be used. @@ -5052,6 +5058,13 @@ public class WindowManagerService extends IWindowManager.Stub } } break; + case RECOMPUTE_FOCUS: { + synchronized (mWindowMap) { + updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, + true /* updateInputWindows */); + } + } + break; } if (DEBUG_WINDOW_TRACE) { Slog.v(TAG_WM, "handleMessage: exit"); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index dac85b345624..c797d8d35db2 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2298,8 +2298,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, "setAnimationLocked: setting mFocusMayChange true"); mService.mFocusMayChange = true; - setDisplayLayoutNeeded(); } + setDisplayLayoutNeeded(); // Window is no longer visible -- make sure if we were waiting // for it to be displayed before enabling the display, that // we allow the display to be enabled now. diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 70287db0e00a..d2479604e92b 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -644,6 +644,11 @@ class WindowSurfacePlacer { ? null : wallpaperTarget; final ArraySet<AppWindowToken> openingApps = mService.mOpeningApps; final ArraySet<AppWindowToken> closingApps = mService.mClosingApps; + final AppWindowToken topOpeningApp = getTopApp(mService.mOpeningApps, + false /* ignoreHidden */); + final AppWindowToken topClosingApp = getTopApp(mService.mClosingApps, + true /* ignoreHidden */); + boolean openingCanBeWallpaperTarget = canBeWallpaperTarget(openingApps); if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New wallpaper target=" + wallpaperTarget @@ -677,13 +682,15 @@ class WindowSurfacePlacer { "New transit: " + AppTransition.appTransitionToString(transit)); } else if (oldWallpaper != null && !mService.mOpeningApps.isEmpty() && !openingApps.contains(oldWallpaper.mAppToken) - && closingApps.contains(oldWallpaper.mAppToken)) { + && closingApps.contains(oldWallpaper.mAppToken) + && topClosingApp == oldWallpaper.mAppToken) { // We are transitioning from an activity with a wallpaper to one without. transit = TRANSIT_WALLPAPER_CLOSE; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: " + AppTransition.appTransitionToString(transit)); } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() && openingApps.contains(wallpaperTarget.mAppToken) + && topOpeningApp == wallpaperTarget.mAppToken && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE) { // We are transitioning from an activity without // a wallpaper to now showing the wallpaper @@ -748,6 +755,31 @@ class WindowSurfacePlacer { return false; } + /** + * Finds the top app in a list of apps, using its {@link AppWindowToken#getPrefixOrderIndex} to + * compare z-order. + * + * @param apps The list of apps to search. + * @param ignoreHidden If set to true, ignores apps that are {@link AppWindowToken#isHidden}. + * @return The top {@link AppWindowToken}. + */ + private AppWindowToken getTopApp(ArraySet<AppWindowToken> apps, boolean ignoreHidden) { + int topPrefixOrderIndex = Integer.MIN_VALUE; + AppWindowToken topApp = null; + for (int i = apps.size() - 1; i >= 0; i--) { + final AppWindowToken app = apps.valueAt(i); + if (ignoreHidden && app.isHidden()) { + continue; + } + final int prefixOrderIndex = app.getPrefixOrderIndex(); + if (prefixOrderIndex > topPrefixOrderIndex) { + topPrefixOrderIndex = prefixOrderIndex; + topApp = app; + } + } + return topApp; + } + private void processApplicationsAnimatingInPlace(int transit) { if (transit == TRANSIT_TASK_IN_PLACE) { // Find the focused window diff --git a/services/net/java/android/net/dns/ResolvUtil.java b/services/net/java/android/net/dns/ResolvUtil.java index a2a6615e5f48..d9d4b96f66d0 100644 --- a/services/net/java/android/net/dns/ResolvUtil.java +++ b/services/net/java/android/net/dns/ResolvUtil.java @@ -16,6 +16,8 @@ package android.net.dns; +import static android.system.OsConstants.AI_ADDRCONFIG; + import android.net.Network; import android.net.NetworkUtils; import android.system.GaiException; @@ -41,12 +43,17 @@ public class ResolvUtil { public static InetAddress[] blockingResolveAllLocally(Network network, String name) throws UnknownHostException { + // Use AI_ADDRCONFIG by default + return blockingResolveAllLocally(network, name, AI_ADDRCONFIG); + } + + public static InetAddress[] blockingResolveAllLocally( + Network network, String name, int aiFlags) throws UnknownHostException { final StructAddrinfo hints = new StructAddrinfo(); - // Unnecessary, but expressly no AI_ADDRCONFIG. - hints.ai_flags = 0; - // Fetch all IP addresses at once to minimize re-resolution. + hints.ai_flags = aiFlags; + // Other hints identical to the default Inet6AddressImpl implementation hints.ai_family = OsConstants.AF_UNSPEC; - hints.ai_socktype = OsConstants.SOCK_DGRAM; + hints.ai_socktype = OsConstants.SOCK_STREAM; final Network networkForResolv = getNetworkWithUseLocalNameserversFlag(network); diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index b4dc94192f24..17babe9635be 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -1628,6 +1628,20 @@ public class NetworkPolicyManagerServiceTest { } } + /** + * Test that policy set of {null, NetworkPolicy, null} does not crash and restores the valid + * NetworkPolicy. + */ + @Test + public void testSetNetworkPolicies_withNullPolicies_doesNotThrow() { + NetworkPolicy[] policies = new NetworkPolicy[3]; + policies[1] = buildDefaultFakeMobilePolicy(); + setNetworkPolicies(policies); + + assertNetworkPolicyEquals(DEFAULT_CYCLE_DAY, mDefaultWarningBytes, mDefaultLimitBytes, + true); + } + private SubscriptionPlan buildMonthlyDataPlan(ZonedDateTime start, long limitBytes) { return SubscriptionPlan.Builder .createRecurringMonthly(start) diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java index 142b950c395b..94e02bc4d35f 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java @@ -448,6 +448,39 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); } + public void testgetHashFactorPrimaryUser() throws RemoteException { + final String password = "password"; + mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, + PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); + final byte[] hashFactor = mService.getHashFactor(password, PRIMARY_USER_ID); + assertNotNull(hashFactor); + + mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, password, + PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID); + final byte[] newHashFactor = mService.getHashFactor(null, PRIMARY_USER_ID); + assertNotNull(newHashFactor); + // Hash factor should never change after password change/removal + assertArrayEquals(hashFactor, newHashFactor); + } + + public void testgetHashFactorManagedProfileUnifiedChallenge() throws RemoteException { + final String pattern = "1236"; + mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, null, + PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID); + mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); + assertNotNull(mService.getHashFactor(null, MANAGED_PROFILE_USER_ID)); + } + + public void testgetHashFactorManagedProfileSeparateChallenge() throws RemoteException { + final String primaryPassword = "primary"; + final String profilePassword = "profile"; + mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, + PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); + mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, + PASSWORD_QUALITY_ALPHABETIC, MANAGED_PROFILE_USER_ID); + assertNotNull(mService.getHashFactor(profilePassword, MANAGED_PROFILE_USER_ID)); + } + public void testPasswordData_serializeDeserialize() { PasswordData data = new PasswordData(); data.scryptN = 11; diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java index dd9a8abff8f1..7bcb5719c357 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java @@ -170,6 +170,31 @@ public class UserManagerTest extends AndroidTestCase { } @MediumTest + public void testSetUserAdmin() throws Exception { + UserInfo userInfo = createUser("SecondaryUser", /*flags=*/ 0); + + // Assert user is not admin and has SMS and calls restrictions. + assertFalse(userInfo.isAdmin()); + assertTrue(mUserManager.hasUserRestriction(UserManager.DISALLOW_SMS, + userInfo.getUserHandle())); + assertTrue(mUserManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, + userInfo.getUserHandle())); + + // Assign admin privileges. + mUserManager.setUserAdmin(userInfo.id); + + // Refresh. + userInfo = mUserManager.getUserInfo(userInfo.id); + + // Verify user became admin and SMS and call restrictions are lifted. + assertTrue(userInfo.isAdmin()); + assertFalse(mUserManager.hasUserRestriction(UserManager.DISALLOW_SMS, + userInfo.getUserHandle())); + assertFalse(mUserManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, + userInfo.getUserHandle())); + } + + @MediumTest public void testGetProfileParent() throws Exception { final int primaryUserId = mUserManager.getPrimaryUser().id; diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java index 845095ae19b9..ac196f9c80dc 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java @@ -329,6 +329,21 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(window1, sWm.mRoot.computeFocusedWindow()); } + @Test + public void testKeyguard_preventsSecondaryDisplayFocus() throws Exception { + final WindowState keyguard = createWindow(null, TYPE_STATUS_BAR, + sWm.getDefaultDisplayContentLocked(), "keyguard"); + assertEquals(keyguard, sWm.mRoot.computeFocusedWindow()); + + // Add a window to a second display, and it should be focused + final DisplayContent dc = createNewDisplay(); + final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win"); + assertEquals(win, sWm.mRoot.computeFocusedWindow()); + + mWmRule.getWindowManagerPolicy().keyguardShowingAndNotOccluded = true; + assertEquals(keyguard, sWm.mRoot.computeFocusedWindow()); + } + /** * This tests setting the maximum ui width on a display. */ 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 765b3d5957c4..50f3fbefaf40 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -51,6 +51,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { private final Supplier<WindowManagerService> mWmSupplier; int rotationToReport = 0; + boolean keyguardShowingAndNotOccluded = false; private Runnable mRunnableWhenAddingSplashScreen; @@ -338,7 +339,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { @Override public boolean isKeyguardLocked() { - return false; + return keyguardShowingAndNotOccluded; } @Override @@ -358,7 +359,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { @Override public boolean isKeyguardShowingAndNotOccluded() { - return false; + return keyguardShowingAndNotOccluded; } @Override diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java index 32bfda32de7f..d91079e3bb1b 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java @@ -59,6 +59,7 @@ import org.mockito.invocation.InvocationOnMock; public class WindowManagerServiceRule implements TestRule { private WindowManagerService mService; + private TestWindowManagerPolicy mPolicy; @Override public Statement apply(Statement base, Description description) { @@ -108,7 +109,7 @@ public class WindowManagerServiceRule implements TestRule { } mService = WindowManagerService.main(context, ims, true, false, - false, new TestWindowManagerPolicy( + false, mPolicy = new TestWindowManagerPolicy( WindowManagerServiceRule.this::getWindowManagerService)); mService.onInitReady(); @@ -132,6 +133,7 @@ public class WindowManagerServiceRule implements TestRule { waitUntilWindowManagerHandlersIdle(); removeServices(); mService = null; + mPolicy = null; } }; } @@ -140,6 +142,10 @@ public class WindowManagerServiceRule implements TestRule { return mService; } + public TestWindowManagerPolicy getWindowManagerPolicy() { + return mPolicy; + } + public void waitUntilWindowManagerHandlersIdle() { final WindowManagerService wm = getWindowManagerService(); if (wm != null) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 3dde7f1b59b3..41b4718e6343 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -91,10 +91,12 @@ import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.Process; +import android.os.RemoteException; import android.os.UserHandle; import android.provider.MediaStore; import android.provider.Settings.Secure; import android.service.notification.Adjustment; +import android.service.notification.INotificationListener; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationStats; import android.service.notification.NotifyingApp; @@ -160,6 +162,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Mock private NotificationUsageStats mUsageStats; @Mock + private UsageStatsManagerInternal mAppUsageStats; + @Mock private AudioManager mAudioManager; @Mock ActivityManager mActivityManager; @@ -276,7 +280,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mPackageManager, mPackageManagerClient, mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, - mGroupHelper, mAm, mock(UsageStatsManagerInternal.class), + mGroupHelper, mAm, mAppUsageStats, mock(DevicePolicyManagerInternal.class)); } catch (SecurityException e) { if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { @@ -3019,4 +3023,46 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(true, mService.canUseManagedServices("d")); } + + @Test + public void testOnNotificationVisibilityChanged_triggersInterruptionUsageStat() { + final NotificationRecord r = generateNotificationRecord( + mTestNotificationChannel, 1, null, true); + r.setTextChanged(true); + mService.addNotification(r); + + mService.mNotificationDelegate.onNotificationVisibilityChanged(new NotificationVisibility[] + {NotificationVisibility.obtain(r.getKey(), 1, 1, true)}, + new NotificationVisibility[]{}); + + verify(mAppUsageStats).reportInterruptiveNotification(anyString(), anyString(), anyInt()); + } + + @Test + public void testSetNotificationsShownFromListener_triggersInterruptionUsageStat() + throws RemoteException { + final NotificationRecord r = generateNotificationRecord( + mTestNotificationChannel, 1, null, true); + r.setTextChanged(true); + mService.addNotification(r); + + mBinderService.setNotificationsShownFromListener(null, new String[] {r.getKey()}); + + verify(mAppUsageStats).reportInterruptiveNotification(anyString(), anyString(), anyInt()); + } + + @Test + public void testMybeRecordInterruptionLocked_doesNotRecordTwice() + throws RemoteException { + final NotificationRecord r = generateNotificationRecord( + mTestNotificationChannel, 1, null, true); + r.setInterruptive(true); + mService.addNotification(r); + + mService.maybeRecordInterruptionLocked(r); + mService.maybeRecordInterruptionLocked(r); + + verify(mAppUsageStats, times(1)).reportInterruptiveNotification( + anyString(), anyString(), anyInt()); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java index e3289ab3e66e..c1868e7da921 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java @@ -602,4 +602,45 @@ public class NotificationRecordTest extends UiServiceTestCase { record.setIsAppImportanceLocked(false); assertEquals(false, record.getIsAppImportanceLocked()); } + + @Test + public void testIsInterruptive_textChanged_notSeen() { + StatusBarNotification sbn = getNotification(false /*preO */, false /* noisy */, + false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, + false /* lights */, false /* defaultLights */, null /* group */); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + assertEquals(false, record.isInterruptive()); + + record.setTextChanged(true); + assertEquals(false, record.isInterruptive()); + } + + @Test + public void testIsInterruptive_textChanged_seen() { + StatusBarNotification sbn = getNotification(false /*preO */, false /* noisy */, + false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, + false /* lights */, false /* defaultLights */, null /* group */); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + assertEquals(false, record.isInterruptive()); + + record.setTextChanged(true); + record.setSeen(); + assertEquals(true, record.isInterruptive()); + } + + @Test + public void testIsInterruptive_textNotChanged_seen() { + StatusBarNotification sbn = getNotification(false /*preO */, false /* noisy */, + false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, + false /* lights */, false /* defaultLights */, null /* group */); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + assertEquals(false, record.isInterruptive()); + + record.setTextChanged(false); + record.setSeen(); + assertEquals(false, record.isInterruptive()); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java index 890595045ae2..98c6ec42207f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java @@ -33,8 +33,10 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -115,7 +117,9 @@ public class RankingHelperTest extends UiServiceTestCase { @Mock PackageManager mPm; @Mock IContentProvider mTestIContentProvider; @Mock Context mContext; + @Mock ZenModeHelper mMockZenModeHelper; + private NotificationManager.Policy mTestNotificationPolicy; private Notification mNotiGroupGSortA; private Notification mNotiGroupGSortB; private Notification mNotiNoGroup; @@ -172,12 +176,12 @@ public class RankingHelperTest extends UiServiceTestCase { when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI))) .thenReturn(SOUND_URI); - ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class); - mHelper = new RankingHelper(getContext(), mPm, mHandler, mockZenModeHelper, + mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, + NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND); + when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); + mHelper = new RankingHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mUsageStats, new String[] {ImportanceExtractor.class.getName()}); - - when(mockZenModeHelper.getNotificationPolicy()).thenReturn(new NotificationManager.Policy( - 0, 0, 0)); + resetZenModeHelper(); mNotiGroupGSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setContentTitle("A") @@ -299,6 +303,11 @@ public class RankingHelperTest extends UiServiceTestCase { return null; } + private void resetZenModeHelper() { + reset(mMockZenModeHelper); + when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); + } + @Test public void testFindAfterRankingWithASplitGroup() throws Exception { ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(3); @@ -1136,44 +1145,86 @@ public class RankingHelperTest extends UiServiceTestCase { public void testCreateAndDeleteCanChannelsBypassDnd() throws Exception { // create notification channel that can't bypass dnd // expected result: areChannelsBypassingDnd = false + // setNotificationPolicy isn't called since areChannelsBypassingDnd was already false NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW); mHelper.createNotificationChannel(PKG, UID, channel, true, false); assertFalse(mHelper.areChannelsBypassingDnd()); + verify(mMockZenModeHelper, never()).setNotificationPolicy(any()); + resetZenModeHelper(); - // create notification channel that can bypass dnd + // create notification channel that can bypass dnd // expected result: areChannelsBypassingDnd = true NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW); channel2.setBypassDnd(true); mHelper.createNotificationChannel(PKG, UID, channel2, true, true); assertTrue(mHelper.areChannelsBypassingDnd()); + verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any()); + resetZenModeHelper(); // delete channels mHelper.deleteNotificationChannel(PKG, UID, channel.getId()); assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND + verify(mMockZenModeHelper, never()).setNotificationPolicy(any()); + resetZenModeHelper(); + mHelper.deleteNotificationChannel(PKG, UID, channel2.getId()); assertFalse(mHelper.areChannelsBypassingDnd()); - + verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any()); + resetZenModeHelper(); } @Test public void testUpdateCanChannelsBypassDnd() throws Exception { // create notification channel that can't bypass dnd // expected result: areChannelsBypassingDnd = false + // setNotificationPolicy isn't called since areChannelsBypassingDnd was already false NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW); mHelper.createNotificationChannel(PKG, UID, channel, true, false); assertFalse(mHelper.areChannelsBypassingDnd()); + verify(mMockZenModeHelper, never()).setNotificationPolicy(any()); + resetZenModeHelper(); // update channel so it CAN bypass dnd: // expected result: areChannelsBypassingDnd = true channel.setBypassDnd(true); mHelper.updateNotificationChannel(PKG, UID, channel, true); assertTrue(mHelper.areChannelsBypassingDnd()); + verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any()); + resetZenModeHelper(); // update channel so it can't bypass dnd: // expected result: areChannelsBypassingDnd = false channel.setBypassDnd(false); mHelper.updateNotificationChannel(PKG, UID, channel, true); assertFalse(mHelper.areChannelsBypassingDnd()); + verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any()); + resetZenModeHelper(); + } + + @Test + public void testSetupNewZenModeHelper_canBypass() { + // start notification policy off with mAreChannelsBypassingDnd = true, but + // RankingHelper should change to false + mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, + NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND); + when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); + mHelper = new RankingHelper(getContext(), mPm, mHandler, mMockZenModeHelper, + mUsageStats, new String[] {ImportanceExtractor.class.getName()}); + assertFalse(mHelper.areChannelsBypassingDnd()); + verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any()); + resetZenModeHelper(); + } + + @Test + public void testSetupNewZenModeHelper_cannotBypass() { + // start notification policy off with mAreChannelsBypassingDnd = false + mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0); + when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); + mHelper = new RankingHelper(getContext(), mPm, mHandler, mMockZenModeHelper, + mUsageStats, new String[] {ImportanceExtractor.class.getName()}); + assertFalse(mHelper.areChannelsBypassingDnd()); + verify(mMockZenModeHelper, never()).setNotificationPolicy(any()); + resetZenModeHelper(); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java new file mode 100644 index 000000000000..c6aea882b7ca --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2018 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.slice; + +import static org.junit.Assert.assertTrue; +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.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; + +import com.android.server.UiServiceTestCase; +import com.android.server.slice.SliceManagerService.PackageMatchingCache; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.function.Supplier; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public class PackageMatchingCacheTest extends UiServiceTestCase { + + private final Supplier<String> supplier = mock(Supplier.class); + private final PackageMatchingCache cache = new PackageMatchingCache(supplier); + + @Test + public void testNulls() { + // Doesn't get for a null input + cache.matches(null); + verify(supplier, never()).get(); + + // Gets once valid input in sent. + cache.matches(""); + verify(supplier).get(); + } + + @Test + public void testCaching() { + when(supplier.get()).thenReturn("ret.pkg"); + + assertTrue(cache.matches("ret.pkg")); + assertTrue(cache.matches("ret.pkg")); + assertTrue(cache.matches("ret.pkg")); + + verify(supplier, times(1)).get(); + } + + @Test + public void testGetOnFailure() { + when(supplier.get()).thenReturn("ret.pkg"); + assertTrue(cache.matches("ret.pkg")); + + when(supplier.get()).thenReturn("other.pkg"); + assertTrue(cache.matches("other.pkg")); + verify(supplier, times(2)).get(); + } +} diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java index da897ae9ea0b..a112fa627f0a 100644 --- a/tests/net/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java @@ -56,6 +56,7 @@ import java.util.Set; @SmallTest public class NetworkCapabilitiesTest { private static final String TEST_SSID = "TEST_SSID"; + private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID"; @Test public void testMaybeMarkCapabilitiesRestricted() { @@ -374,6 +375,12 @@ public class NetworkCapabilitiesTest { assertFalse(nc1.satisfiedByNetworkCapabilities(nc2)); } + private ArraySet<UidRange> uidRange(int from, int to) { + final ArraySet<UidRange> range = new ArraySet<>(1); + range.add(new UidRange(from, to)); + return range; + } + @Test public void testCombineCapabilities() { NetworkCapabilities nc1 = new NetworkCapabilities(); @@ -400,14 +407,30 @@ public class NetworkCapabilitiesTest { nc2.combineCapabilities(nc1); assertTrue(TEST_SSID.equals(nc2.getSSID())); - // Because they now have the same SSID, the folllowing call should not throw + // Because they now have the same SSID, the following call should not throw nc2.combineCapabilities(nc1); - nc1.setSSID("different " + TEST_SSID); + nc1.setSSID(DIFFERENT_TEST_SSID); try { nc2.combineCapabilities(nc1); fail("Expected IllegalStateException: can't combine different SSIDs"); } catch (IllegalStateException expected) {} + nc1.setSSID(TEST_SSID); + + nc1.setUids(uidRange(10, 13)); + assertNotEquals(nc1, nc2); + nc2.combineCapabilities(nc1); // Everything + 10~13 is still everything. + assertNotEquals(nc1, nc2); + nc1.combineCapabilities(nc2); // 10~13 + everything is everything. + assertEquals(nc1, nc2); + nc1.setUids(uidRange(10, 13)); + nc2.setUids(uidRange(20, 23)); + assertNotEquals(nc1, nc2); + nc1.combineCapabilities(nc2); + assertTrue(nc1.appliesToUid(12)); + assertFalse(nc2.appliesToUid(12)); + assertTrue(nc1.appliesToUid(22)); + assertTrue(nc2.appliesToUid(22)); } @Test @@ -446,4 +469,38 @@ public class NetworkCapabilitiesTest { p.setDataPosition(0); assertEquals(NetworkCapabilities.CREATOR.createFromParcel(p), netCap); } + + @Test + public void testSet() { + NetworkCapabilities nc1 = new NetworkCapabilities(); + NetworkCapabilities nc2 = new NetworkCapabilities(); + + nc1.addUnwantedCapability(NET_CAPABILITY_CAPTIVE_PORTAL); + nc1.addCapability(NET_CAPABILITY_NOT_ROAMING); + assertNotEquals(nc1, nc2); + nc2.set(nc1); + assertEquals(nc1, nc2); + assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING)); + assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_CAPTIVE_PORTAL)); + + // This will effectively move NOT_ROAMING capability from required to unwanted for nc1. + nc1.addUnwantedCapability(NET_CAPABILITY_NOT_ROAMING); + nc1.setSSID(TEST_SSID); + nc2.set(nc1); + assertEquals(nc1, nc2); + // Contrary to combineCapabilities, set() will have removed the NOT_ROAMING capability + // from nc2. + assertFalse(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING)); + assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING)); + assertTrue(TEST_SSID.equals(nc2.getSSID())); + + nc1.setSSID(DIFFERENT_TEST_SSID); + nc2.set(nc1); + assertEquals(nc1, nc2); + assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSSID())); + + nc1.setUids(uidRange(10, 13)); + nc2.set(nc1); // Overwrites, as opposed to combineCapabilities + assertEquals(nc1, nc2); + } } diff --git a/tests/net/java/android/net/captiveportal/CaptivePortalProbeSpecTest.java b/tests/net/java/android/net/captiveportal/CaptivePortalProbeSpecTest.java new file mode 100644 index 000000000000..40a8b3eb9793 --- /dev/null +++ b/tests/net/java/android/net/captiveportal/CaptivePortalProbeSpecTest.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2018 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.net.captiveportal; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.MalformedURLException; +import java.net.URL; +import java.text.ParseException; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class CaptivePortalProbeSpecTest { + + @Test + public void testGetResult_Regex() throws MalformedURLException, ParseException { + // 2xx status or 404, with an empty (match everything) location regex + CaptivePortalProbeSpec statusRegexSpec = CaptivePortalProbeSpec.parseSpec( + "http://www.google.com@@/@@2[0-9]{2}|404@@/@@"); + + // 404, or 301/302 redirect to some HTTPS page under google.com + CaptivePortalProbeSpec redirectSpec = CaptivePortalProbeSpec.parseSpec( + "http://google.com@@/@@404|30[12]@@/@@https://([0-9a-z]+\\.)*google\\.com.*"); + + assertSuccess(statusRegexSpec.getResult(200, null)); + assertSuccess(statusRegexSpec.getResult(299, "qwer")); + assertSuccess(statusRegexSpec.getResult(404, null)); + assertSuccess(statusRegexSpec.getResult(404, "")); + + assertPortal(statusRegexSpec.getResult(300, null)); + assertPortal(statusRegexSpec.getResult(399, "qwer")); + assertPortal(statusRegexSpec.getResult(500, null)); + + assertSuccess(redirectSpec.getResult(404, null)); + assertSuccess(redirectSpec.getResult(404, "")); + assertSuccess(redirectSpec.getResult(301, "https://www.google.com")); + assertSuccess(redirectSpec.getResult(301, "https://www.google.com/test?q=3")); + assertSuccess(redirectSpec.getResult(302, "https://google.com/test?q=3")); + + assertPortal(redirectSpec.getResult(299, "https://google.com/test?q=3")); + assertPortal(redirectSpec.getResult(299, "")); + assertPortal(redirectSpec.getResult(499, null)); + assertPortal(redirectSpec.getResult(301, "http://login.portal.example.com/loginpage")); + assertPortal(redirectSpec.getResult(302, "http://www.google.com/test?q=3")); + } + + @Test(expected = ParseException.class) + public void testParseSpec_Empty() throws MalformedURLException, ParseException { + CaptivePortalProbeSpec.parseSpec(""); + } + + @Test(expected = ParseException.class) + public void testParseSpec_Null() throws MalformedURLException, ParseException { + CaptivePortalProbeSpec.parseSpec(null); + } + + @Test(expected = ParseException.class) + public void testParseSpec_MissingParts() throws MalformedURLException, ParseException { + CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123"); + } + + @Test(expected = ParseException.class) + public void testParseSpec_TooManyParts() throws MalformedURLException, ParseException { + CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123@@/@@456@@/@@extra"); + } + + @Test(expected = ParseException.class) + public void testParseSpec_InvalidStatusRegex() throws MalformedURLException, ParseException { + CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@unmatched(parenthesis@@/@@456"); + } + + @Test(expected = ParseException.class) + public void testParseSpec_InvalidLocationRegex() throws MalformedURLException, ParseException { + CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123@@/@@unmatched[[]bracket"); + } + + @Test(expected = MalformedURLException.class) + public void testParseSpec_EmptyURL() throws MalformedURLException, ParseException { + CaptivePortalProbeSpec.parseSpec("@@/@@123@@/@@123"); + } + + @Test(expected = ParseException.class) + public void testParseSpec_NoParts() throws MalformedURLException, ParseException { + CaptivePortalProbeSpec.parseSpec("invalid"); + } + + @Test(expected = MalformedURLException.class) + public void testParseSpec_RegexInvalidUrl() throws MalformedURLException, ParseException { + CaptivePortalProbeSpec.parseSpec("notaurl@@/@@123@@/@@123"); + } + + @Test + public void testParseSpecOrNull_UsesSpec() { + final String specUrl = "http://google.com/probe"; + final String redirectUrl = "https://google.com/probe"; + CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull( + specUrl + "@@/@@302@@/@@" + redirectUrl); + assertEquals(specUrl, spec.getUrl().toString()); + + assertPortal(spec.getResult(302, "http://portal.example.com")); + assertSuccess(spec.getResult(302, redirectUrl)); + } + + @Test + public void testParseSpecOrNull_UsesFallback() throws MalformedURLException { + CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull(null); + assertNull(spec); + + spec = CaptivePortalProbeSpec.parseSpecOrNull(""); + assertNull(spec); + + spec = CaptivePortalProbeSpec.parseSpecOrNull("@@/@@ @@/@@ @@/@@"); + assertNull(spec); + + spec = CaptivePortalProbeSpec.parseSpecOrNull("invalid@@/@@123@@/@@456"); + assertNull(spec); + } + + @Test + public void testParseSpecOrUseStatusCodeFallback_EmptySpec() throws MalformedURLException { + CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull(""); + assertNull(spec); + } + + private void assertIsStatusSpec(CaptivePortalProbeSpec spec) { + assertSuccess(spec.getResult(204, null)); + assertSuccess(spec.getResult(204, "1234")); + + assertPortal(spec.getResult(200, null)); + assertPortal(spec.getResult(301, null)); + assertPortal(spec.getResult(302, "1234")); + assertPortal(spec.getResult(399, "")); + + assertFailed(spec.getResult(404, null)); + assertFailed(spec.getResult(500, "1234")); + } + + private void assertPortal(CaptivePortalProbeResult result) { + assertTrue(result.isPortal()); + } + + private void assertSuccess(CaptivePortalProbeResult result) { + assertTrue(result.isSuccessful()); + } + + private void assertFailed(CaptivePortalProbeResult result) { + assertTrue(result.isFailed()); + } +} diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 5b73bbaa1c73..220858081e93 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -112,6 +112,8 @@ import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.StringNetworkSpecifier; import android.net.UidRange; +import android.net.VpnService; +import android.net.captiveportal.CaptivePortalProbeResult; import android.net.metrics.IpConnectivityLog; import android.net.util.MultinetworkPolicyTracker; import android.os.ConditionVariable; @@ -133,6 +135,7 @@ import android.test.mock.MockContentResolver; import android.util.ArraySet; import android.util.Log; +import com.android.internal.net.VpnConfig; import com.android.internal.util.ArrayUtils; import com.android.internal.util.WakeupMessage; import com.android.internal.util.test.BroadcastInterceptingContext; @@ -196,13 +199,13 @@ public class ConnectivityServiceTest { private MockNetworkAgent mWiFiNetworkAgent; private MockNetworkAgent mCellNetworkAgent; private MockNetworkAgent mEthernetNetworkAgent; + private MockVpn mMockVpn; private Context mContext; @Mock IpConnectivityMetrics.Logger mMetricsService; @Mock DefaultNetworkMetrics mDefaultNetworkMetrics; @Mock INetworkManagementService mNetworkManagementService; @Mock INetworkStatsService mStatsService; - @Mock Vpn mMockVpn; private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class); @@ -478,6 +481,14 @@ public class ConnectivityServiceTest { mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); } + public void setNetworkCapabilities(NetworkCapabilities nc, + boolean sendToConnectivityService) { + mNetworkCapabilities.set(nc); + if (sendToConnectivityService) { + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + } + public void connectWithoutInternet() { mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); mNetworkAgent.sendNetworkInfo(mNetworkInfo); @@ -593,6 +604,10 @@ public class ConnectivityServiceTest { return mRedirectUrl; } + public NetworkAgent getNetworkAgent() { + return mNetworkAgent; + } + public NetworkCapabilities getNetworkCapabilities() { return mNetworkCapabilities; } @@ -725,6 +740,87 @@ public class ConnectivityServiceTest { } } + private static Looper startHandlerThreadAndReturnLooper() { + final HandlerThread handlerThread = new HandlerThread("MockVpnThread"); + handlerThread.start(); + return handlerThread.getLooper(); + } + + private class MockVpn extends Vpn { + // TODO : the interactions between this mock and the mock network agent are too + // hard to get right at this moment, because it's unclear in which case which + // target needs to get a method call or both, and in what order. It's because + // MockNetworkAgent wants to manage its own NetworkCapabilities, but the Vpn + // parent class of MockVpn agent wants that responsibility. + // That being said inside the test it should be possible to make the interactions + // harder to get wrong with precise speccing, judicious comments, helper methods + // and a few sprinkled assertions. + + private boolean mConnected = false; + // Careful ! This is different from mNetworkAgent, because MockNetworkAgent does + // not inherit from NetworkAgent. + private MockNetworkAgent mMockNetworkAgent; + + public MockVpn(int userId) { + super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService, + userId); + } + + public void setNetworkAgent(MockNetworkAgent agent) { + waitForIdle(agent, TIMEOUT_MS); + mMockNetworkAgent = agent; + mNetworkAgent = agent.getNetworkAgent(); + mNetworkCapabilities.set(agent.getNetworkCapabilities()); + } + + public void setUids(Set<UidRange> uids) { + mNetworkCapabilities.setUids(uids); + updateCapabilities(); + } + + @Override + public int getNetId() { + return mMockNetworkAgent.getNetwork().netId; + } + + @Override + public boolean appliesToUid(int uid) { + return mConnected; // Trickery to simplify testing. + } + + @Override + protected boolean isCallerEstablishedOwnerLocked() { + return mConnected; // Similar trickery + } + + public void connect() { + mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities()); + mConnected = true; + mConfig = new VpnConfig(); + } + + @Override + public void updateCapabilities() { + if (!mConnected) return; + super.updateCapabilities(); + // Because super.updateCapabilities will update the capabilities of the agent but not + // the mock agent, the mock agent needs to know about them. + copyCapabilitiesToNetworkAgent(); + } + + private void copyCapabilitiesToNetworkAgent() { + if (null != mMockNetworkAgent) { + mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities, + false /* sendToConnectivityService */); + } + } + + public void disconnect() { + mConnected = false; + mConfig = null; + } + } + private class FakeWakeupMessage extends WakeupMessage { private static final int UNREASONABLY_LONG_WAIT = 1000; @@ -893,10 +989,12 @@ public class ConnectivityServiceTest { public void mockVpn(int uid) { synchronized (mVpns) { + int userId = UserHandle.getUserId(uid); + mMockVpn = new MockVpn(userId); // This has no effect unless the VPN is actually connected, because things like // getActiveNetworkForUidInternal call getNetworkAgentInfoForNetId on the VPN // netId, and check if that network is actually connected. - mVpns.put(UserHandle.getUserId(Process.myUid()), mMockVpn); + mVpns.put(userId, mMockVpn); } } @@ -926,7 +1024,6 @@ public class ConnectivityServiceTest { MockitoAnnotations.initMocks(this); when(mMetricsService.defaultNetworkMetrics()).thenReturn(mDefaultNetworkMetrics); - when(mMockVpn.appliesToUid(Process.myUid())).thenReturn(true); // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not. // http://b/25897652 . @@ -1548,7 +1645,8 @@ public class ConnectivityServiceTest { void expectCapabilitiesLike(Predicate<NetworkCapabilities> fn, MockNetworkAgent agent) { CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent); - assertTrue(fn.test((NetworkCapabilities) cbi.arg)); + assertTrue("Received capabilities don't match expectations : " + cbi.arg, + fn.test((NetworkCapabilities) cbi.arg)); } void assertNoCallback() { @@ -2576,9 +2674,10 @@ public class ConnectivityServiceTest { final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); final ArraySet<UidRange> ranges = new ArraySet<>(); ranges.add(new UidRange(uid, uid)); - when(mMockVpn.getNetId()).thenReturn(vpnNetworkAgent.getNetwork().netId); - vpnNetworkAgent.setUids(ranges); + mMockVpn.setNetworkAgent(vpnNetworkAgent); + mMockVpn.setUids(ranges); vpnNetworkAgent.connect(true); + mMockVpn.connect(); defaultNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); @@ -4107,9 +4206,10 @@ public class ConnectivityServiceTest { final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); final ArraySet<UidRange> ranges = new ArraySet<>(); ranges.add(new UidRange(uid, uid)); - when(mMockVpn.getNetId()).thenReturn(vpnNetworkAgent.getNetwork().netId); - vpnNetworkAgent.setUids(ranges); + mMockVpn.setNetworkAgent(vpnNetworkAgent); + mMockVpn.setUids(ranges); vpnNetworkAgent.connect(false); + mMockVpn.connect(); genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent); genericNotVpnNetworkCallback.assertNoCallback(); @@ -4141,7 +4241,7 @@ public class ConnectivityServiceTest { defaultCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent); ranges.add(new UidRange(uid, uid)); - vpnNetworkAgent.setUids(ranges); + mMockVpn.setUids(ranges); genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent); genericNotVpnNetworkCallback.assertNoCallback(); @@ -4191,9 +4291,10 @@ public class ConnectivityServiceTest { MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); final ArraySet<UidRange> ranges = new ArraySet<>(); ranges.add(new UidRange(uid, uid)); - when(mMockVpn.getNetId()).thenReturn(vpnNetworkAgent.getNetwork().netId); - vpnNetworkAgent.setUids(ranges); + mMockVpn.setNetworkAgent(vpnNetworkAgent); + mMockVpn.setUids(ranges); vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */); + mMockVpn.connect(); defaultCallback.assertNoCallback(); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); @@ -4202,9 +4303,10 @@ public class ConnectivityServiceTest { defaultCallback.assertNoCallback(); vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); - when(mMockVpn.getNetId()).thenReturn(vpnNetworkAgent.getNetwork().netId); - vpnNetworkAgent.setUids(ranges); + mMockVpn.setNetworkAgent(vpnNetworkAgent); + mMockVpn.setUids(ranges); vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */); + mMockVpn.connect(); defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); @@ -4213,13 +4315,111 @@ public class ConnectivityServiceTest { defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); - when(mMockVpn.getNetId()).thenReturn(vpnNetworkAgent.getNetwork().netId); ranges.clear(); - vpnNetworkAgent.setUids(ranges); - + mMockVpn.setNetworkAgent(vpnNetworkAgent); + mMockVpn.setUids(ranges); vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */); + mMockVpn.connect(); defaultCallback.assertNoCallback(); mCm.unregisterNetworkCallback(defaultCallback); } + + @Test + public void testVpnSetUnderlyingNetworks() { + final int uid = Process.myUid(); + + final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback(); + final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_VPN) + .addTransportType(TRANSPORT_VPN) + .build(); + NetworkCapabilities nc; + mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); + vpnNetworkCallback.assertNoCallback(); + + final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + final ArraySet<UidRange> ranges = new ArraySet<>(); + ranges.add(new UidRange(uid, uid)); + mMockVpn.setNetworkAgent(vpnNetworkAgent); + mMockVpn.connect(); + mMockVpn.setUids(ranges); + vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */); + + vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent); + nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork()); + assertTrue(nc.hasTransport(TRANSPORT_VPN)); + assertFalse(nc.hasTransport(TRANSPORT_CELLULAR)); + assertFalse(nc.hasTransport(TRANSPORT_WIFI)); + // For safety reasons a VPN without underlying networks is considered metered. + assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED)); + + // Connect cell and use it as an underlying network. + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + + mService.setUnderlyingNetworksForVpn( + new Network[] { mCellNetworkAgent.getNetwork() }); + + vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED), + vpnNetworkAgent); + + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mWiFiNetworkAgent.connect(true); + + mService.setUnderlyingNetworksForVpn( + new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); + + vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED), + vpnNetworkAgent); + + // Don't disconnect, but note the VPN is not using wifi any more. + mService.setUnderlyingNetworksForVpn( + new Network[] { mCellNetworkAgent.getNetwork() }); + + vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED), + vpnNetworkAgent); + + // Use Wifi but not cell. Note the VPN is now unmetered. + mService.setUnderlyingNetworksForVpn( + new Network[] { mWiFiNetworkAgent.getNetwork() }); + + vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN) + && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) + && caps.hasCapability(NET_CAPABILITY_NOT_METERED), + vpnNetworkAgent); + + // Use both again. + mService.setUnderlyingNetworksForVpn( + new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); + + vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED), + vpnNetworkAgent); + + // Disconnect cell. Receive update without even removing the dead network from the + // underlying networks – it's dead anyway. Not metered any more. + mCellNetworkAgent.disconnect(); + vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN) + && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) + && caps.hasCapability(NET_CAPABILITY_NOT_METERED), + vpnNetworkAgent); + + // Disconnect wifi too. No underlying networks means this is now metered. + mWiFiNetworkAgent.disconnect(); + vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN) + && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED), + vpnNetworkAgent); + + mMockVpn.disconnect(); + } } |