diff options
120 files changed, 2281 insertions, 766 deletions
diff --git a/api/current.txt b/api/current.txt index 157a72c47f05..899381bec02d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -30508,7 +30508,7 @@ package android.os { field public static final int M = 23; // 0x17 field public static final int N = 24; // 0x18 field public static final int N_MR1 = 25; // 0x19 - field public static final int O = 26; // 0x1a + field public static final int O = 10000; // 0x2710 } public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable { diff --git a/api/system-current.txt b/api/system-current.txt index 110d1dd59661..8aa6c362480d 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -33218,7 +33218,7 @@ package android.os { field public static final int M = 23; // 0x17 field public static final int N = 24; // 0x18 field public static final int N_MR1 = 25; // 0x19 - field public static final int O = 26; // 0x1a + field public static final int O = 10000; // 0x2710 } public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable { diff --git a/api/test-current.txt b/api/test-current.txt index ef2fce7afc82..944a35078e51 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -30617,7 +30617,7 @@ package android.os { field public static final int M = 23; // 0x17 field public static final int N = 24; // 0x18 field public static final int N_MR1 = 25; // 0x19 - field public static final int O = 26; // 0x1a + field public static final int O = 10000; // 0x2710 } public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable { diff --git a/cmds/vr/src/com/android/commands/vr/Vr.java b/cmds/vr/src/com/android/commands/vr/Vr.java index bf97bbaa019b..b765866faef9 100644 --- a/cmds/vr/src/com/android/commands/vr/Vr.java +++ b/cmds/vr/src/com/android/commands/vr/Vr.java @@ -16,7 +16,7 @@ package com.android.commands.vr; -import android.app.CompatibilityDisplayProperties; +import android.app.Vr2dDisplayProperties; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; @@ -39,7 +39,7 @@ public final class Vr extends BaseCommand { private static final String COMMAND_SET_PERSISTENT_VR_MODE_ENABLED = "set-persistent-vr-mode-enabled"; - private static final String COMMAND_SET_COMPATIBILITY_DISPLAY_PROPERTIES = + private static final String COMMAND_SET_VR2D_DISPLAY_PROPERTIES = "set-display-props"; private IVrManager mVrService; @@ -63,8 +63,8 @@ public final class Vr extends BaseCommand { String command = nextArgRequired(); switch (command) { - case COMMAND_SET_COMPATIBILITY_DISPLAY_PROPERTIES: - runSetCompatibilityDisplayProperties(); + case COMMAND_SET_VR2D_DISPLAY_PROPERTIES: + runSetVr2dDisplayProperties(); break; case COMMAND_SET_PERSISTENT_VR_MODE_ENABLED: runSetPersistentVrModeEnabled(); @@ -74,7 +74,7 @@ public final class Vr extends BaseCommand { } } - private void runSetCompatibilityDisplayProperties() throws RemoteException { + private void runSetVr2dDisplayProperties() throws RemoteException { String widthStr = nextArgRequired(); int width = Integer.parseInt(widthStr); @@ -84,11 +84,11 @@ public final class Vr extends BaseCommand { String dpiStr = nextArgRequired(); int dpi = Integer.parseInt(dpiStr); - CompatibilityDisplayProperties compatDisplayProperties = - new CompatibilityDisplayProperties(width, height, dpi); + Vr2dDisplayProperties vr2dDisplayProperties = + new Vr2dDisplayProperties(width, height, dpi); try { - mVrService.setCompatibilityDisplayProperties(compatDisplayProperties); + mVrService.setVr2dDisplayProperties(vr2dDisplayProperties); } catch (RemoteException re) { System.err.println("Error: Can't set persistent mode " + re); } diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 65cb5f492285..0dfaf6a0da3a 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -244,8 +244,8 @@ public abstract class ActivityManagerInternal { /** * Called after virtual display Id is updated by - * {@link com.android.server.vr.CompatibilityDisplay} with a specific - * {@param compatibilityDisplayId}. + * {@link com.android.server.vr.Vr2dDisplay} with a specific + * {@param vr2dDisplayId}. */ - public abstract void setVrCompatibilityDisplayId(int vrCompatibilityDisplayId); + public abstract void setVr2dDisplayId(int vr2dDisplayId); } diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index f369955cdc31..4994fbb0da3a 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -39,8 +39,11 @@ oneway interface ITaskStackListener { * Called whenever IActivityManager.startActivity is called on an activity that is already * running in the pinned stack and the activity is not actually started, but the task is either * brought to the front or a new Intent is delivered to it. + * + * @param clearedTask whether or not the launch activity also cleared the task as a part of + * starting */ - void onPinnedActivityRestartAttempt(); + void onPinnedActivityRestartAttempt(boolean clearedTask); /** * Called whenever the pinned stack is starting animating a resize. diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index c8b8c6c1b262..2e56bcf8a3de 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -4358,6 +4358,8 @@ public class Notification implements Parcelable mN.mLargeIcon = null; Bitmap largeIconLegacy = mN.largeIcon; mN.largeIcon = null; + ArrayList<Action> actions = mActions; + mActions = new ArrayList<>(); Bundle publicExtras = new Bundle(); publicExtras.putBoolean(EXTRA_SHOW_WHEN, savedBundle.getBoolean(EXTRA_SHOW_WHEN)); @@ -4373,6 +4375,7 @@ public class Notification implements Parcelable mN.extras = savedBundle; mN.mLargeIcon = largeIcon; mN.largeIcon = largeIconLegacy; + mActions = actions; mStyle = style; return view; } diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index 307fc9128ed2..2df011fb856e 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -39,7 +39,7 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override - public void onPinnedActivityRestartAttempt() throws RemoteException { + public void onPinnedActivityRestartAttempt(boolean clearedTask) throws RemoteException { } @Override diff --git a/core/java/android/app/CompatibilityDisplayProperties.aidl b/core/java/android/app/Vr2dDisplayProperties.aidl index 626a63e05819..1e049433c052 100644 --- a/core/java/android/app/CompatibilityDisplayProperties.aidl +++ b/core/java/android/app/Vr2dDisplayProperties.aidl @@ -17,4 +17,4 @@ package android.app; /** @hide */ -parcelable CompatibilityDisplayProperties; +parcelable Vr2dDisplayProperties; diff --git a/core/java/android/app/CompatibilityDisplayProperties.java b/core/java/android/app/Vr2dDisplayProperties.java index 9a9bc2c40d94..a608bb08836f 100644 --- a/core/java/android/app/CompatibilityDisplayProperties.java +++ b/core/java/android/app/Vr2dDisplayProperties.java @@ -27,7 +27,7 @@ import java.io.PrintWriter; * * @hide */ -public class CompatibilityDisplayProperties implements Parcelable { +public class Vr2dDisplayProperties implements Parcelable { /** * The actual width, height and dpi. @@ -36,7 +36,7 @@ public class CompatibilityDisplayProperties implements Parcelable { private final int mHeight; private final int mDpi; - public CompatibilityDisplayProperties(int width, int height, int dpi) { + public Vr2dDisplayProperties(int width, int height, int dpi) { mWidth = width; mHeight = height; mDpi = dpi; @@ -52,7 +52,7 @@ public class CompatibilityDisplayProperties implements Parcelable { @Override public String toString() { - return "CompatibilityDisplayProperties{" + + return "Vr2dDisplayProperties{" + "mWidth=" + mWidth + ", mHeight=" + mHeight + ", mDpi=" + mDpi + @@ -64,7 +64,7 @@ public class CompatibilityDisplayProperties implements Parcelable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - CompatibilityDisplayProperties that = (CompatibilityDisplayProperties) o; + Vr2dDisplayProperties that = (Vr2dDisplayProperties) o; if (getWidth() != that.getWidth()) return false; if (getHeight() != that.getHeight()) return false; @@ -83,27 +83,27 @@ public class CompatibilityDisplayProperties implements Parcelable { dest.writeInt(mDpi); } - public static final Parcelable.Creator<CompatibilityDisplayProperties> CREATOR - = new Parcelable.Creator<CompatibilityDisplayProperties>() { + public static final Parcelable.Creator<Vr2dDisplayProperties> CREATOR + = new Parcelable.Creator<Vr2dDisplayProperties>() { @Override - public CompatibilityDisplayProperties createFromParcel(Parcel source) { - return new CompatibilityDisplayProperties(source); + public Vr2dDisplayProperties createFromParcel(Parcel source) { + return new Vr2dDisplayProperties(source); } @Override - public CompatibilityDisplayProperties[] newArray(int size) { - return new CompatibilityDisplayProperties[size]; + public Vr2dDisplayProperties[] newArray(int size) { + return new Vr2dDisplayProperties[size]; } }; - private CompatibilityDisplayProperties(Parcel source) { + private Vr2dDisplayProperties(Parcel source) { mWidth = source.readInt(); mHeight = source.readInt(); mDpi = source.readInt(); } public void dump(PrintWriter pw, String prefix) { - pw.println(prefix + "CompatibilityDisplayProperties:"); + pw.println(prefix + "Vr2dDisplayProperties:"); pw.println(prefix + " width=" + mWidth); pw.println(prefix + " height=" + mHeight); pw.println(prefix + " dpi=" + mDpi); diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java index 878c8c38516d..040b330c2f30 100644 --- a/core/java/android/app/VrManager.java +++ b/core/java/android/app/VrManager.java @@ -45,20 +45,20 @@ public class VrManager { } /** - * Sets the resolution and DPI of the compatibility virtual display used to display 2D + * Sets the resolution and DPI of the vr2d virtual display used to display 2D * applications in VR mode. * * <p>Requires {@link android.Manifest.permission#ACCESS_VR_MANAGER} permission.</p> * - * @param {@link android.app.CompatibilityDisplayProperties} properties to be set to the - * virtual display for 2D applications in VR mode. + * @param vr2dDisplayProp properties to be set to the virtual display for + * 2D applications in VR mode. * * {@hide} */ - public void setCompatibilityDisplayProperties( - CompatibilityDisplayProperties compatDisplayProp) { + public void setVr2dDisplayProperties( + Vr2dDisplayProperties vr2dDisplayProp) { try { - mService.setCompatibilityDisplayProperties(compatDisplayProp); + mService.setVr2dDisplayProperties(vr2dDisplayProp); } catch (RemoteException e) { e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 33535302bd5c..7261dfa09bb4 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -1559,14 +1559,14 @@ public class AssistStructure implements Parcelable { @Override public void setText(CharSequence text) { ViewNodeText t = getNodeText(); - t.mText = text; + t.mText = TextUtils.trimNoCopySpans(text); t.mTextSelectionStart = t.mTextSelectionEnd = -1; } @Override public void setText(CharSequence text, int selectionStart, int selectionEnd) { ViewNodeText t = getNodeText(); - t.mText = text; + t.mText = TextUtils.trimNoCopySpans(text); t.mTextSelectionStart = selectionStart; t.mTextSelectionEnd = selectionEnd; } @@ -1737,13 +1737,6 @@ public class AssistStructure implements Parcelable { } @Override - public void setUrl(String url) { - if (url == null) return; - - setWebDomain(url); - } - - @Override public void setWebDomain(@Nullable String domain) { if (domain == null) { mNode.mWebDomain = null; diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index d1ad8de0b113..c58eaa14ed82 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -136,6 +136,38 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public static final int STATE_NOT_PLAYING = 11; + /** + * We don't have a stored preference for whether or not the given A2DP sink device supports + * optional codecs. + * @hide */ + public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1; + + /** + * The given A2DP sink device does not support optional codecs. + * @hide */ + public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0; + + /** + * The given A2DP sink device does support optional codecs. + * @hide */ + public static final int OPTIONAL_CODECS_SUPPORTED = 1; + + /** + * We don't have a stored preference for whether optional codecs should be enabled or disabled + * for the given A2DP device. + * @hide */ + public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1; + + /** + * Optional codecs should be disabled for the given A2DP device. + * @hide */ + public static final int OPTIONAL_CODECS_PREF_DISABLED = 0; + + /** + * Optional codecs should be enabled for the given A2DP device. + * @hide */ + public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; + private Context mContext; private ServiceListener mServiceListener; private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); @@ -655,6 +687,88 @@ public final class BluetoothA2dp implements BluetoothProfile { } /** + * Returns whether this device supports optional codecs. + * + * @param device The device to check + * @return one of OPTIONAL_CODECS_SUPPORT_UNKNOWN, OPTIONAL_CODECS_NOT_SUPPORTED, or + * OPTIONAL_CODECS_SUPPORTED. + * + * @hide + */ + public int supportsOptionalCodecs(BluetoothDevice device) { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() && isValidDevice(device)) { + return mService.supportsOptionalCodecs(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return OPTIONAL_CODECS_SUPPORT_UNKNOWN; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e); + return OPTIONAL_CODECS_SUPPORT_UNKNOWN; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Returns whether this device should have optional codecs enabled. + * + * @param device The device in question. + * @return one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or + * OPTIONAL_CODECS_PREF_DISABLED. + * + * @hide + */ + public int getOptionalCodecsEnabled(BluetoothDevice device) { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() && isValidDevice(device)) { + return mService.getOptionalCodecsEnabled(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return OPTIONAL_CODECS_PREF_UNKNOWN; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e); + return OPTIONAL_CODECS_PREF_UNKNOWN; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Sets a persistent preference for whether a given device should have optional codecs enabled. + * + * @param device The device to set this preference for. + * @param value Whether the optional codecs should be enabled for this device. This should be + * one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or + * OPTIONAL_CODECS_PREF_DISABLED. + * @hide + */ + public void setOptionalCodecsEnabled(BluetoothDevice device, int value) { + try { + if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN && + value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED && + value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { + Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value); + return; + } + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { + mService.setOptionalCodecsEnabled(device, value); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** * Helper for converting a state to a string. * * For debug use only - strings are not internationalized. diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl index a775a1f90b8e..1b533cba3d2a 100644 --- a/core/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl @@ -42,4 +42,7 @@ interface IBluetoothA2dp { oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig); oneway void enableOptionalCodecs(); oneway void disableOptionalCodecs(); + int supportsOptionalCodecs(in BluetoothDevice device); + int getOptionalCodecsEnabled(in BluetoothDevice device); + oneway void setOptionalCodecsEnabled(in BluetoothDevice device, int value); } diff --git a/core/java/android/net/INetworkScoreService.aidl b/core/java/android/net/INetworkScoreService.aidl index 73e52c89dcc4..d163a4441097 100644 --- a/core/java/android/net/INetworkScoreService.aidl +++ b/core/java/android/net/INetworkScoreService.aidl @@ -45,7 +45,7 @@ interface INetworkScoreService * Set the active scorer and clear existing scores. * @param packageName the package name of the new scorer to use. * @return true if the operation succeeded, or false if the new package is not a valid scorer. - * @throws SecurityException if the caller is not the system. + * @throws SecurityException if the caller is not the system or a network scorer. */ boolean setActiveScorer(in String packageName); diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java index eeb426a4bcf9..9f6e45ca6fb5 100644 --- a/core/java/android/net/NetworkScoreManager.java +++ b/core/java/android/net/NetworkScoreManager.java @@ -16,27 +16,20 @@ package android.net; -import static android.net.NetworkRecommendationProvider.EXTRA_RECOMMENDATION_RESULT; - +import android.Manifest.permission; import android.annotation.IntDef; -import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.content.Context; -import android.os.Bundle; -import android.os.Handler; -import android.os.RemoteCallback; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; -import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; -import java.util.concurrent.CompletableFuture; /** * Class that manages communication between network subsystems and a network scorer. @@ -49,9 +42,9 @@ import java.util.concurrent.CompletableFuture; * * <p>A network scorer is any application which: * <ul> - * <li>Declares the {@link android.Manifest.permission#SCORE_NETWORKS} permission. + * <li>Declares the {@link permission#SCORE_NETWORKS} permission. * <li>Include a Service for the {@link #ACTION_RECOMMEND_NETWORKS} action - * protected by the {@link android.Manifest.permission#BIND_NETWORK_RECOMMENDATION_SERVICE} + * protected by the {@link permission#BIND_NETWORK_RECOMMENDATION_SERVICE} * permission. * </ul> * @@ -319,7 +312,7 @@ public class NetworkScoreManager { * * @return true if the operation succeeded, or false if the new package is not a valid scorer. * @throws SecurityException if the caller is not a system process or does not hold the - * {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission + * {@link permission#SCORE_NETWORKS} permission * @hide */ @SystemApi @@ -351,7 +344,7 @@ public class NetworkScoreManager { * * @return true if the broadcast was sent, or false if there is no active scorer. * @throws SecurityException if the caller does not hold the - * {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission. + * {@link permission#REQUEST_NETWORK_SCORES} permission. * @hide */ public boolean requestScores(NetworkKey[] networks) throws SecurityException { @@ -368,7 +361,7 @@ public class NetworkScoreManager { * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}. * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores. * @throws SecurityException if the caller does not hold the - * {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission. + * {@link permission#REQUEST_NETWORK_SCORES} permission. * @throws IllegalArgumentException if a score cache is already registered for this type. * @deprecated equivalent to registering for cache updates with CACHE_FILTER_NONE. * @hide @@ -385,7 +378,7 @@ public class NetworkScoreManager { * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores * @param filterType the {@link CacheUpdateFilter} to apply * @throws SecurityException if the caller does not hold the - * {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission. + * {@link permission#REQUEST_NETWORK_SCORES} permission. * @throws IllegalArgumentException if a score cache is already registered for this type. * @hide */ @@ -404,7 +397,7 @@ public class NetworkScoreManager { * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}. * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores. * @throws SecurityException if the caller does not hold the - * {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission. + * {@link permission#REQUEST_NETWORK_SCORES} permission. * @throws IllegalArgumentException if a score cache is already registered for this type. * @hide */ @@ -417,25 +410,6 @@ public class NetworkScoreManager { } /** - * Request a recommendation for which network to connect to. - * - * <p>It is not safe to call this method from the main thread. - * - * @param request a {@link RecommendationRequest} instance containing additional - * request details - * @return a {@link RecommendationResult} instance containing the recommended network - * to connect to - * @throws SecurityException if the caller does not hold the - * {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission. - * @hide - * @deprecated to be removed. - */ - public RecommendationResult requestRecommendation(RecommendationRequest request) - throws SecurityException { - return null; - } - - /** * Determine whether the application with the given UID is the enabled scorer. * * @param callingUid the UID to check diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 4bad7ab76be6..86fcfc8fb961 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -758,7 +758,7 @@ public class Build { /** * O. */ - public static final int O = 26; + public static final int O = CUR_DEVELOPMENT; // STOPSHIP Replace with the real version. } /** The type of build, like "user" or "eng". */ diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java index 1ef3916a743f..8302eceb772a 100644 --- a/core/java/android/os/VintfObject.java +++ b/core/java/android/os/VintfObject.java @@ -26,17 +26,12 @@ public class VintfObject { private static final String LOG_TAG = "VintfObject"; /** - * Slurps all device information (both manifests) - * and report it. + * Slurps all device information (both manifests and both matrices) + * and report them. * If any error in getting one of the manifests, it is not included in * the list. */ - public static String[] report() { - ArrayList<String> ret = new ArrayList<>(); - put(ret, getDeviceManifest(), "device manifest"); - put(ret, getFrameworkManifest(), "framework manifest"); - return ret.toArray(new String[0]); - } + public static native String[] report(); /** * Verify that the given metadata for an OTA package is compatible with @@ -50,15 +45,4 @@ public class VintfObject { */ public static native int verify(String[] packageInfo); - // return null if any error, otherwise XML string. - private static native String getDeviceManifest(); - private static native String getFrameworkManifest(); - - private static void put(ArrayList<String> list, String content, String message) { - if (content == null || content.length() == 0) { - Log.e(LOG_TAG, "Cannot get;" + message + "; check native logs for details."); - return; - } - list.add(content); - } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index f215ae7ce405..1f423ea66063 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6789,7 +6789,8 @@ public final class Settings { * Represented as milliseconds from midnight (e.g. 79200000 == 10pm). * @hide */ - public static final String NIGHT_DISPLAY_CUSTOM_START_TIME = "night_display_custom_start_time"; + public static final String NIGHT_DISPLAY_CUSTOM_START_TIME = + "night_display_custom_start_time"; /** * Custom time when Night display is scheduled to deactivate. @@ -6799,6 +6800,14 @@ public final class Settings { public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time"; /** + * Time in milliseconds (since epoch) when Night display was last activated. Use to decide + * whether to apply the current activated state after a reboot or user change. + * @hide + */ + public static final String NIGHT_DISPLAY_LAST_ACTIVATED_TIME = + "night_display_last_activated_time"; + + /** * Names of the service components that the current user has explicitly allowed to * be a VR mode listener, separated by ':'. * @@ -7024,6 +7033,7 @@ public final class Settings { NIGHT_DISPLAY_CUSTOM_END_TIME, NIGHT_DISPLAY_COLOR_TEMPERATURE, NIGHT_DISPLAY_AUTO_MODE, + NIGHT_DISPLAY_LAST_ACTIVATED_TIME, NIGHT_DISPLAY_ACTIVATED, SYNC_PARENT_SOUNDS, CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED, @@ -8971,6 +8981,12 @@ public final class Settings { public static final String BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX = "bluetooth_a2dp_src_priority_"; /** {@hide} */ + public static final String BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX = + "bluetooth_a2dp_supports_optional_codecs_"; + /** {@hide} */ + public static final String BLUETOOTH_A2DP_OPTIONAL_CODECS_ENABLED_PREFIX = + "bluetooth_a2dp_optional_codecs_enabled_"; + /** {@hide} */ public static final String BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX = "bluetooth_input_device_priority_"; /** {@hide} */ @@ -9237,6 +9253,25 @@ public final class Settings { } /** + * Get the key that retrieves a bluetooth a2dp device's ability to support optional codecs. + * @hide + */ + public static final String getBluetoothA2dpSupportsOptionalCodecsKey(String address) { + return BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX + + address.toUpperCase(Locale.ROOT); + } + + /** + * Get the key that retrieves whether a bluetooth a2dp device should have optional codecs + * enabled. + * @hide + */ + public static final String getBluetoothA2dpOptionalCodecsEnabledKey(String address) { + return BLUETOOTH_A2DP_OPTIONAL_CODECS_ENABLED_PREFIX + + address.toUpperCase(Locale.ROOT); + } + + /** * Get the key that retrieves a bluetooth Input Device's priority. * @hide */ @@ -10379,6 +10414,15 @@ public final class Settings { public static final String MAX_NOTIFICATION_ENQUEUE_RATE = "max_notification_enqueue_rate"; /** + * Displays toasts when an app posts a notification that does not specify a valid channel. + * + * The value 1 - enable, 0 - disable + * @hide + */ + public static final String SHOW_NOTIFICATION_CHANNEL_WARNINGS = + "show_notification_channel_warnings"; + + /** * Whether cell is enabled/disabled * @hide */ diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl index 8b2d0c6293e8..fc8afe9131b1 100644 --- a/core/java/android/service/vr/IVrManager.aidl +++ b/core/java/android/service/vr/IVrManager.aidl @@ -16,7 +16,7 @@ package android.service.vr; -import android.app.CompatibilityDisplayProperties; +import android.app.Vr2dDisplayProperties; import android.service.vr.IVrStateCallbacks; import android.service.vr.IPersistentVrStateCallbacks; @@ -68,16 +68,16 @@ interface IVrManager { void setPersistentVrModeEnabled(in boolean enabled); /** - * Sets the resolution and DPI of the compatibility virtual display used to display + * Sets the resolution and DPI of the vr2d virtual display used to display * 2D applications in VR mode. * * <p>Requires {@link android.Manifest.permission#ACCESS_VR_MANAGER} permission.</p> * - * @param compatDisplayProperties Compatibitlity display properties to be set for + * @param vr2dDisplayProperties Vr2d display properties to be set for * the VR virtual display */ - void setCompatibilityDisplayProperties( - in CompatibilityDisplayProperties compatDisplayProperties); + void setVr2dDisplayProperties( + in Vr2dDisplayProperties vr2dDisplayProperties); /** * Return current virtual display id. @@ -85,7 +85,7 @@ interface IVrManager { * @return {@link android.view.Display.INVALID_DISPLAY} if there is no virtual display * currently, else return the display id of the virtual display */ - int getCompatibilityDisplayId(); + int getVr2dDisplayId(); /** * Initiate connection for system controller data. diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 081deebaed77..91f43d632dfd 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -1921,6 +1921,22 @@ public class TextUtils { return false; } + /** + * If the {@code charSequence} is instance of {@link Spanned}, creates a new copy and + * {@link NoCopySpan}'s are removed from the copy. Otherwise the given {@code charSequence} is + * returned as it is. + * + * @hide + */ + @Nullable + public static CharSequence trimNoCopySpans(@Nullable CharSequence charSequence) { + if (charSequence != null && charSequence instanceof Spanned) { + // SpannableStringBuilder copy constructor trims NoCopySpans. + return new SpannableStringBuilder(charSequence); + } + return charSequence; + } + private static Object sLock = new Object(); private static char[] sTemp = null; diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java index f71589c6c5ef..6bdc9ff5d0fa 100644 --- a/core/java/android/view/ViewStructure.java +++ b/core/java/android/view/ViewStructure.java @@ -374,13 +374,6 @@ public abstract class ViewStructure { public abstract Rect getTempRect(); /** - * @deprecated - use {@link #setWebDomain(String)} instead. - * @hide - */ - @Deprecated - public abstract void setUrl(String url); - - /** * Sets the Web domain represented by this node. * * <p>Typically used when the view is a container for an HTML document. diff --git a/core/java/android/view/autofill/AutofillValue.java b/core/java/android/view/autofill/AutofillValue.java index b57dab56318e..3beae11cf38c 100644 --- a/core/java/android/view/autofill/AutofillValue.java +++ b/core/java/android/view/autofill/AutofillValue.java @@ -26,6 +26,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; import android.view.View; import com.android.internal.util.Preconditions; @@ -257,7 +258,8 @@ public final class AutofillValue implements Parcelable { * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info. */ public static AutofillValue forText(@Nullable CharSequence value) { - return value == null ? null : new AutofillValue(AUTOFILL_TYPE_TEXT, value); + return value == null ? null : new AutofillValue(AUTOFILL_TYPE_TEXT, + TextUtils.trimNoCopySpans(value)); } /** diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index ebf1c0189b15..209ff097f9e6 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -607,6 +607,7 @@ final class TextClassifierImpl implements TextClassifier { @Nullable public static Intent create(Context context, String type, String text) { type = type.trim().toLowerCase(Locale.ENGLISH); + text = text.trim(); switch (type) { case TextClassifier.TYPE_EMAIL: return new Intent(Intent.ACTION_SENDTO) @@ -618,6 +619,9 @@ final class TextClassifierImpl implements TextClassifier { return new Intent(Intent.ACTION_VIEW) .setData(Uri.parse(String.format("geo:0,0?q=%s", text))); case TextClassifier.TYPE_URL: + if (!text.startsWith("https://") && !text.startsWith("http://")) { + text = "http://" + text; + } return new Intent(Intent.ACTION_VIEW, Uri.parse(text)) .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()); default: diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index ad3a99d3610f..b0d63951971b 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -1404,6 +1404,11 @@ public class Editor { // or double-clicks that could "dismiss" the floating toolbar. int delay = ViewConfiguration.getDoubleTapTimeout(); mTextView.postDelayed(mShowFloatingToolbar, delay); + + // This classifies the text and most likely returns before the toolbar is actually + // shown. If not, it will update the toolbar with the result when classification + // returns. We would rather not wait for a long running classification process. + invalidateActionModeAsync(); } } @@ -1853,7 +1858,7 @@ public class Editor { mInsertionPointCursorController.invalidateHandle(); } if (mTextActionMode != null) { - invalidateActionModeAsync(); + invalidateActionMode(); } } @@ -1945,12 +1950,12 @@ public class Editor { if (mRestartActionModeOnNextRefresh) { // To avoid distraction, newly start action mode only when selection action // mode is being restarted. - startSelectionActionMode(); + startSelectionActionModeAsync(false); } } else if (selectionController == null || !selectionController.isActive()) { // Insertion action mode is active. Avoid dismissing the selection. stopTextActionModeWithPreservingSelection(); - startSelectionActionMode(); + startSelectionActionModeAsync(false); } else { mTextActionMode.invalidateContentRect(); } @@ -2004,22 +2009,24 @@ public class Editor { /** * Asynchronously starts a selection action mode using the TextClassifier. */ - void startSelectionActionModeAsync() { - getSelectionActionModeHelper().startActionModeAsync(); + void startSelectionActionModeAsync(boolean adjustSelection) { + getSelectionActionModeHelper().startActionModeAsync(adjustSelection); } /** - * Synchronously starts a selection action mode without the TextClassifier. + * Asynchronously invalidates an action mode using the TextClassifier. */ - void startSelectionActionMode() { - getSelectionActionModeHelper().startActionMode(); + private void invalidateActionModeAsync() { + getSelectionActionModeHelper().invalidateActionModeAsync(); } /** - * Asynchronously invalidates an action mode using the TextClassifier. + * Synchronously invalidates an action mode without the TextClassifier. */ - private void invalidateActionModeAsync() { - getSelectionActionModeHelper().invalidateActionModeAsync(); + private void invalidateActionMode() { + if (mTextActionMode != null) { + mTextActionMode.invalidate(); + } } private SelectionActionModeHelper getSelectionActionModeHelper() { @@ -2075,7 +2082,7 @@ public class Editor { } if (mTextActionMode != null) { // Text action mode is already started - invalidateActionModeAsync(); + invalidateActionMode(); return false; } @@ -4703,7 +4710,7 @@ public class Editor { } positionAtCursorOffset(offset, false); if (mTextActionMode != null) { - invalidateActionModeAsync(); + invalidateActionMode(); } } @@ -4787,7 +4794,7 @@ public class Editor { } updateDrawable(); if (mTextActionMode != null) { - invalidateActionModeAsync(); + invalidateActionMode(); } } @@ -5414,13 +5421,8 @@ public class Editor { resetDragAcceleratorState(); if (mTextView.hasSelection()) { - // Do not invoke the text assistant if this was a drag selection. - if (mHaventMovedEnoughToStartDrag) { - startSelectionActionModeAsync(); - } else { - startSelectionActionMode(); - } - + // Drag selection should not be adjusted by the text classifier. + startSelectionActionModeAsync(mHaventMovedEnoughToStartDrag); } break; } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 1457d026ea39..569fe017ac86 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -49,7 +49,6 @@ import android.view.accessibility.AccessibilityNodeProvider; import android.widget.RemoteViews.RemoteView; import com.android.internal.R; -import com.android.internal.util.Preconditions; import com.google.android.collect.Lists; @@ -336,11 +335,6 @@ public class ListView extends AbsListView { * @param isSelectable whether the item is selectable */ public void addHeaderView(View v, Object data, boolean isSelectable) { - Preconditions.checkState( - v.getParent() == null, - "The specified child already has a parent. " - + "You must call removeView() on the child's parent first."); - final FixedViewInfo info = new FixedViewInfo(); info.view = v; info.data = data; @@ -435,11 +429,6 @@ public class ListView extends AbsListView { * @param isSelectable true if the footer view can be selected */ public void addFooterView(View v, Object data, boolean isSelectable) { - Preconditions.checkState( - v.getParent() == null, - "The specified child already has a parent. " - + "You must call removeView() on the child's parent first."); - final FixedViewInfo info = new FixedViewInfo(); info.view = v; info.data = data; diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index c9d172fa78b5..16a1087510d3 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -65,7 +65,7 @@ final class SelectionActionModeHelper { textView.getTextClassifier(), textView.getText(), 0, 1, textView.getTextLocales()); } - public void startActionModeAsync() { + public void startActionModeAsync(boolean adjustSelection) { cancelAsyncTask(); if (isNoOpTextClassifier() || !hasSelection()) { // No need to make an async call for a no-op TextClassifier. @@ -74,16 +74,16 @@ final class SelectionActionModeHelper { } else { resetTextClassificationHelper(); mTextClassificationAsyncTask = new TextClassificationAsyncTask( - mEditor.getTextView(), TIMEOUT_DURATION, - mTextClassificationHelper::suggestSelection, this::startActionMode) + mEditor.getTextView(), + TIMEOUT_DURATION, + adjustSelection + ? mTextClassificationHelper::suggestSelection + : mTextClassificationHelper::classifyText, + this::startActionMode) .execute(); } } - public void startActionMode() { - startActionMode(null); - } - public void invalidateActionModeAsync() { cancelAsyncTask(); if (isNoOpTextClassifier() || !hasSelection()) { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 242dcf535d32..eaf111521574 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -10579,7 +10579,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Selection.setSelection((Spannable) text, start, end); // Make sure selection mode is engaged. if (mEditor != null) { - mEditor.startSelectionActionMode(); + mEditor.startSelectionActionModeAsync(false); } return true; } diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp index 9491a1ecdad3..033f2dfe5173 100644 --- a/core/jni/android_os_VintfObject.cpp +++ b/core/jni/android_os_VintfObject.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * 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. @@ -16,6 +16,10 @@ #define LOG_TAG "VintfObject" //#define LOG_NDEBUG 0 +#include <android-base/logging.h> + +#include <vector> +#include <string> #include <JNIHelp.h> #include <vintf/VintfObject.h> @@ -23,31 +27,48 @@ #include "core_jni_helpers.h" +static jclass gString; + namespace android { -using vintf::HalManifest; -using vintf::RuntimeInfo; using vintf::VintfObject; using vintf::gHalManifestConverter; +using vintf::gCompatibilityMatrixConverter; +using vintf::XmlConverter; -static jstring android_os_VintfObject_getDeviceManifest(JNIEnv* env, jclass clazz) -{ - const HalManifest *manifest = VintfObject::GetDeviceHalManifest(); - if (manifest == nullptr) { - return nullptr; +static inline jobjectArray toJavaStringArray(JNIEnv* env, const std::vector<std::string>& v) { + jobjectArray ret = env->NewObjectArray(v.size(), gString, NULL /* init element */); + for (size_t i = 0; i < v.size(); ++i) { + env->SetObjectArrayElement(ret, i, env->NewStringUTF(v[i].c_str())); } - std::string xml = gHalManifestConverter(*manifest); - return env->NewStringUTF(xml.c_str()); + return ret; } -static jstring android_os_VintfObject_getFrameworkManifest(JNIEnv* env, jclass clazz) -{ - const HalManifest *manifest = VintfObject::GetFrameworkHalManifest(); - if (manifest == nullptr) { - return nullptr; +template<typename T> +static void tryAddSchema(const T* object, const XmlConverter<T>& converter, + const std::string& description, + std::vector<std::string>* cStrings) { + if (object == nullptr) { + LOG(WARNING) << __FUNCTION__ << "Cannot get " << description; + } else { + cStrings->push_back(converter(*object)); } - std::string xml = gHalManifestConverter(*manifest); - return env->NewStringUTF(xml.c_str()); +} + +static jobjectArray android_os_VintfObject_report(JNIEnv* env, jclass clazz) +{ + std::vector<std::string> cStrings; + + tryAddSchema(VintfObject::GetDeviceHalManifest(), gHalManifestConverter, + "device manifest", &cStrings); + tryAddSchema(VintfObject::GetFrameworkHalManifest(), gHalManifestConverter, + "framework manifest", &cStrings); + tryAddSchema(VintfObject::GetDeviceCompatibilityMatrix(), gCompatibilityMatrixConverter, + "device compatibility matrix", &cStrings); + tryAddSchema(VintfObject::GetFrameworkCompatibilityMatrix(), gCompatibilityMatrixConverter, + "framework compatibility matrix", &cStrings); + + return toJavaStringArray(env, cStrings); } static jint android_os_VintfObject_verify(JNIEnv *env, jclass clazz, jobjectArray packageInfo) { @@ -66,15 +87,18 @@ static jint android_os_VintfObject_verify(JNIEnv *env, jclass clazz, jobjectArra // ---------------------------------------------------------------------------- static const JNINativeMethod gVintfObjectMethods[] = { - {"getDeviceManifest", "()Ljava/lang/String;", (void*)android_os_VintfObject_getDeviceManifest}, - {"getFrameworkManifest", "()Ljava/lang/String;", (void*)android_os_VintfObject_getFrameworkManifest}, - {"verify", "([Ljava/lang/String;)I", (void*)android_os_VintfObject_verify}, + {"report", "()[Ljava/lang/String;", (void*)android_os_VintfObject_report}, + {"verify", "([Ljava/lang/String;)I", (void*)android_os_VintfObject_verify}, }; + const char* const kVintfObjectPathName = "android/os/VintfObject"; int register_android_os_VintfObject(JNIEnv* env) { + + gString = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/String")); + return RegisterMethodsOrDie(env, kVintfObjectPathName, gVintfObjectMethods, NELEM(gVintfObjectMethods)); } diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto index ce951defadbc..ea40fd5bb472 100644 --- a/core/proto/android/providers/settings.proto +++ b/core/proto/android/providers/settings.proto @@ -329,6 +329,8 @@ message GlobalSettingsProto { SettingProto max_notification_enqueue_rate = 284; SettingProto cell_on = 285; SettingProto network_recommendations_package = 286; + SettingProto bluetooth_a2dp_supports_optional_codecs_prefix = 287; + SettingProto bluetooth_a2dp_optional_codecs_enabled_prefix = 288; } message SecureSettingsProto { diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index a921fd740b7e..aeb564b49ea8 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1299,9 +1299,9 @@ * Include a Service for the android.net.scoring.RECOMMEND_NETWORKS action protected by the BIND_NETWORK_RECOMMENDATION_SERVICE permission. - This must be set to a valid network recommendation app. + This must be set to a valid network recommendation app or empty. --> - <string name="config_defaultNetworkRecommendationProviderPackage" translatable="false">com.android.networkrecommendation</string> + <string name="config_defaultNetworkRecommendationProviderPackage" translatable="false"></string> <!-- Whether to enable Hardware FLP overlay which allows Hardware FLP to be replaced by an app at run-time. When disabled, only the @@ -2872,4 +2872,8 @@ <!-- Additional non-platform defined secure settings exposed to Instant Apps --> <string-array name="config_allowedSecureInstantAppSettings"></string-array> + + <!-- Handle volume keys directly in Window Manager without passing them to the foreground app --> + <bool name="config_handleVolumeKeysInWindowManager">false</bool> + </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 1966f6a89412..cff6eb198473 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3010,4 +3010,5 @@ <java-symbol type="array" name="config_allowedSystemInstantAppSettings" /> <java-symbol type="array" name="config_allowedSecureInstantAppSettings" /> + <java-symbol type="bool" name="config_handleVolumeKeysInWindowManager" /> </resources> diff --git a/core/tests/coretests/src/android/os/VintfObjectTest.java b/core/tests/coretests/src/android/os/VintfObjectTest.java index aaaf55ccf799..821ee806743d 100644 --- a/core/tests/coretests/src/android/os/VintfObjectTest.java +++ b/core/tests/coretests/src/android/os/VintfObjectTest.java @@ -26,5 +26,8 @@ public class VintfObjectTest extends TestCase { // From /system/manifest.xml assertTrue(String.join("", xmls).contains( "<manifest version=\"1.0\" type=\"framework\">")); + // From /system/compatibility-matrix.xml + assertTrue(String.join("", xmls).contains( + "<compatibility-matrix version=\"1.0\" type=\"framework\">")); } } diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 99909acb9c0a..aeed20e211a2 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -109,6 +109,8 @@ public class SettingsBackupTest { Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, Settings.Global.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX, Settings.Global.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX, + Settings.Global.BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX, + Settings.Global.BLUETOOTH_A2DP_OPTIONAL_CODECS_ENABLED_PREFIX, Settings.Global.BLUETOOTH_DISABLED_PROFILES, Settings.Global.BLUETOOTH_HEADSET_PRIORITY_PREFIX, Settings.Global.BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX, diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java index f59e4fc188d4..742fd60e25f7 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java @@ -118,7 +118,7 @@ public class TextClassificationManagerTest { if (isTextClassifierDisabled()) return; String text = "Visit http://www.android.com for more information"; - String classifiedText = "http://www.android.com"; + String classifiedText = "www.android.com"; int startIndex = text.indexOf(classifiedText); int endIndex = startIndex + classifiedText.length(); assertThat(mClassifier.classifyText(text, startIndex, endIndex, LOCALES), @@ -193,7 +193,19 @@ public class TextClassificationManagerTest { public boolean matches(Object o) { if (o instanceof TextClassification) { TextClassification result = (TextClassification) o; - return text.equals(result.getText()) + final boolean typeRequirementSatisfied; + switch (type) { + case TextClassifier.TYPE_URL: + String scheme = result.getIntent().getData().getScheme(); + typeRequirementSatisfied = "http".equalsIgnoreCase(scheme) + || "https".equalsIgnoreCase(scheme); + break; + default: + typeRequirementSatisfied = true; + } + + return typeRequirementSatisfied + && text.equals(result.getText()) && result.getEntityCount() > 0 && type.equals(result.getEntity(0)); // TODO: Include other properties. diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp index bd798e8dc688..fcdd814bb63e 100644 --- a/libs/hwui/tests/unit/FrameBuilderTests.cpp +++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp @@ -819,7 +819,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferColorOp_unbounded) { EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected"; } -OPENGL_PIPELINE_TEST(FrameBuilder, renderNode) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderNode) { class RenderNodeTestRenderer : public TestRendererBase { public: void onRectOp(const RectOp& op, const BakedOpState& state) override { @@ -2321,7 +2321,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clip_replace) { EXPECT_EQ(1, renderer.getIndex()); } -OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedInMiddle) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedInMiddle) { /* R is backward projected on B A / \ @@ -2351,7 +2351,7 @@ OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedInMiddle) { EXPECT_EQ(3, renderer.getIndex()); } -OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectLast) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectLast) { /* R is backward projected on E A / | \ @@ -2383,7 +2383,7 @@ OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectLast) { EXPECT_EQ(4, renderer.getIndex()); } -OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderNoReceivable) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderNoReceivable) { /* R is backward projected without receiver A / \ @@ -2412,7 +2412,7 @@ OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderNoReceivable) { EXPECT_EQ(2, renderer.getIndex()); } -OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderParentReceivable) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderParentReceivable) { /* R is backward projected on C A / \ @@ -2441,7 +2441,7 @@ OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderParentReceivable) { EXPECT_EQ(3, renderer.getIndex()); } -OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderSameNodeReceivable) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderSameNodeReceivable) { auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { drawOrderedNode(&canvas, 0, nullptr); //nodeB @@ -2464,7 +2464,7 @@ OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderSameNodeReceivable) { EXPECT_EQ(2, renderer.getIndex()); } -OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling) { //TODO: this test together with the next "projectionReorderProjectedSibling2" likely expose a //bug in HWUI. First test draws R, while the second test does not draw R for a nearly identical //tree setup. The correct behaviour is to not draw R, because the receiver cannot be a sibling @@ -2497,7 +2497,7 @@ OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling) { EXPECT_EQ(3, renderer.getIndex()); } -OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling2) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling2) { /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed. A | @@ -2530,7 +2530,7 @@ OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling2) { EXPECT_EQ(3, renderer.getIndex()); } -OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderGrandparentReceivable) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderGrandparentReceivable) { /* R is backward projected on B A | @@ -2562,7 +2562,7 @@ OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderGrandparentReceivable) { EXPECT_EQ(3, renderer.getIndex()); } -OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivables) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivables) { /* B and G are receivables, R is backward projected A / \ @@ -2595,7 +2595,7 @@ OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivables) { EXPECT_EQ(4, renderer.getIndex()); } -OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario) { /* B and G are receivables, G is backward projected A / \ @@ -2628,7 +2628,7 @@ OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario EXPECT_EQ(4, renderer.getIndex()); } -OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesDeeper) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesDeeper) { /* B and G are receivables, R is backward projected A / \ diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java index 3831cf7285da..dbfeefdfb037 100644 --- a/obex/javax/obex/ServerSession.java +++ b/obex/javax/obex/ServerSession.java @@ -104,7 +104,6 @@ public final class ServerSession extends ObexSession implements Runnable { case ObexHelper.OBEX_OPCODE_DISCONNECT: handleDisconnectRequest(); - done = true; break; case ObexHelper.OBEX_OPCODE_GET: diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml index 2ef1cf59ffc5..c30913393075 100644 --- a/packages/CarrierDefaultApp/AndroidManifest.xml +++ b/packages/CarrierDefaultApp/AndroidManifest.xml @@ -34,6 +34,7 @@ <intent-filter> <action android:name="com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED" /> <action android:name="com.android.internal.telephony.CARRIER_SIGNAL_RESET" /> + <action android:name="android.intent.action.LOCALE_CHANGED" /> </intent-filter> </receiver> <service android:name="com.android.carrierdefaultapp.ProvisionObserver" diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java index 7fd16019267e..021330650975 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java @@ -112,8 +112,6 @@ public class CarrierActionUtils { private static void onShowCaptivePortalNotification(Intent intent, Context context) { logd("onShowCaptivePortalNotification"); - final NotificationManager notificationMgr = context.getSystemService( - NotificationManager.class); Intent portalIntent = new Intent(context, CaptivePortalLoginActivity.class); portalIntent.putExtras(intent); portalIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT @@ -123,7 +121,8 @@ public class CarrierActionUtils { Notification notification = getNotification(context, R.string.portal_notification_id, R.string.portal_notification_detail, pendingIntent); try { - notificationMgr.notify(PORTAL_NOTIFICATION_TAG, PORTAL_NOTIFICATION_ID, notification); + context.getSystemService(NotificationManager.class) + .notify(PORTAL_NOTIFICATION_TAG, PORTAL_NOTIFICATION_ID, notification); } catch (NullPointerException npe) { loge("setNotificationVisible: " + npe); } @@ -131,12 +130,11 @@ public class CarrierActionUtils { private static void onShowNoDataServiceNotification(Context context) { logd("onShowNoDataServiceNotification"); - final NotificationManager notificationMgr = context.getSystemService( - NotificationManager.class); Notification notification = getNotification(context, R.string.no_data_notification_id, R.string.no_data_notification_detail, null); try { - notificationMgr.notify(NO_DATA_NOTIFICATION_TAG, NO_DATA_NOTIFICATION_ID, notification); + context.getSystemService(NotificationManager.class) + .notify(NO_DATA_NOTIFICATION_TAG, NO_DATA_NOTIFICATION_ID, notification); } catch (NullPointerException npe) { loge("setNotificationVisible: " + npe); } @@ -144,26 +142,16 @@ public class CarrierActionUtils { private static void onCancelAllNotifications(Context context) { logd("onCancelAllNotifications"); - final NotificationManager notificationMgr = context.getSystemService( - NotificationManager.class); - notificationMgr.cancelAll(); + context.getSystemService(NotificationManager.class).cancelAll(); } private static Notification getNotification(Context context, int titleId, int textId, PendingIntent pendingIntent) { final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class); - final NotificationManager notificationManager = context.getSystemService( - NotificationManager.class); final Resources resources = context.getResources(); final Bundle extras = Bundle.forPair(Notification.EXTRA_SUBSTITUTE_APP_NAME, resources.getString(R.string.android_system_label)); - /* Creates the notification channel and registers it with NotificationManager. If a channel - * with the same ID is already registered, NotificationManager will ignore this call. - */ - notificationManager.createNotificationChannel(new NotificationChannel( - NOTIFICATION_CHANNEL_ID_MOBILE_DATA_STATUS, - resources.getString(R.string.mobile_data_status_notification_channel_name), - NotificationManager.IMPORTANCE_DEFAULT)); + createNotificationChannels(context); Notification.Builder builder = new Notification.Builder(context) .setContentTitle(resources.getString(titleId)) .setContentText(String.format(resources.getString(textId), @@ -187,6 +175,19 @@ public class CarrierActionUtils { return builder.build(); } + /** + * Creates the notification channel and registers it with NotificationManager. Also used to + * update an existing channel's name. + */ + static void createNotificationChannels(Context context) { + context.getSystemService(NotificationManager.class) + .createNotificationChannel(new NotificationChannel( + NOTIFICATION_CHANNEL_ID_MOBILE_DATA_STATUS, + context.getResources().getString( + R.string.mobile_data_status_notification_channel_name), + NotificationManager.IMPORTANCE_DEFAULT)); + } + private static void logd(String s) { Log.d(TAG, s); } diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierDefaultBroadcastReceiver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierDefaultBroadcastReceiver.java index 3fd89d97617e..3f55ff514913 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierDefaultBroadcastReceiver.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierDefaultBroadcastReceiver.java @@ -32,6 +32,10 @@ public class CarrierDefaultBroadcastReceiver extends BroadcastReceiver{ Log.d(TAG, "skip carrier actions during provisioning"); return; } + if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) { + CarrierActionUtils.createNotificationChannels(context); + return; + } List<Integer> actionList = CustomConfigLoader.loadCarrierActionList(context, intent); for (int actionIdx : actionList) { Log.d(TAG, "apply carrier action idx: " + actionIdx); diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 5a178a50408b..eb513e170286 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -998,7 +998,8 @@ public class AccessPoint implements Comparable<AccessPoint> { if (mRssi != info.getRssi()) { mRssi = info.getRssi(); updated = true; - } else if (mNetworkInfo.getDetailedState() != networkInfo.getDetailedState()) { + } else if (mNetworkInfo != null && networkInfo != null + && mNetworkInfo.getDetailedState() != networkInfo.getDetailedState()) { updated = true; } mInfo = info; diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index 646b6ba84ebb..be15e657d1c3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -124,7 +124,8 @@ public class WifiTracker { */ private final Object mLock = new Object(); - //visible to both worker and main thread. Guarded by #mInternalAccessPoints + //visible to both worker and main thread. + @GuardedBy("mLock") private final AccessPointListenerAdapter mAccessPointListenerAdapter = new AccessPointListenerAdapter(); @@ -1005,12 +1006,13 @@ public class WifiTracker { if (DBG) { Log.d(TAG, "Starting to copy AP items on the MainHandler"); } - if (notifyListeners) { - notificationMap = mAccessPointListenerAdapter.mPendingNotifications.clone(); - } - - mAccessPointListenerAdapter.mPendingNotifications.clear(); synchronized (mLock) { + if (notifyListeners) { + notificationMap = mAccessPointListenerAdapter.mPendingNotifications.clone(); + } + + mAccessPointListenerAdapter.mPendingNotifications.clear(); + for (AccessPoint internalAccessPoint : mInternalAccessPoints) { AccessPoint accessPoint = oldAccessPoints.get(internalAccessPoint.mId); if (accessPoint == null) { diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java index f519a906eab5..d4ce40c7c0a8 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java @@ -465,9 +465,9 @@ public class WifiTrackerTest { private void updateScoresAndWaitForAccessPointsChangedCallback() throws InterruptedException { // Updating scores can happen together or one after the other, so the latch countdown is set // to 2. - mAccessPointsChangedLatch = new CountDownLatch(3); + mAccessPointsChangedLatch = new CountDownLatch(2); updateScores(); - assertTrue("onAccessPointChanged was not called three times", + assertTrue("onAccessPointChanged was not called twice", mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 885573e86ac6..c7c2222f8230 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -696,6 +696,12 @@ class SettingsProtoDumpUtil { Settings.Global.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX, GlobalSettingsProto.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX); dumpSetting(s, p, + Settings.Global.BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX, + GlobalSettingsProto.BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX); + dumpSetting(s, p, + Settings.Global.BLUETOOTH_A2DP_OPTIONAL_CODECS_ENABLED_PREFIX, + GlobalSettingsProto.BLUETOOTH_A2DP_OPTIONAL_CODECS_ENABLED_PREFIX); + dumpSetting(s, p, Settings.Global.BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX, GlobalSettingsProto.BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX); dumpSetting(s, p, diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java index 7d78c089b089..9ef05c5d4125 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java @@ -16,6 +16,7 @@ package com.android.systemui.plugins.qs; import android.content.Context; import android.graphics.drawable.Drawable; +import android.metrics.LogMaker; import android.service.quicksettings.Tile; import com.android.systemui.plugins.annotations.DependsOn; @@ -66,6 +67,10 @@ public interface QSTile { State getState(); + default LogMaker populate(LogMaker logMaker) { + return logMaker; + } + @ProvidesInterface(version = Callback.VERSION) public interface Callback { public static final int VERSION = 1; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java index 7139d59c6eef..af02e5beca92 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java @@ -37,6 +37,7 @@ public class DozeLog { private static final int PULSE_REASONS = 5; + public static final int PULSE_REASON_NONE = -1; public static final int PULSE_REASON_INTENT = 0; public static final int PULSE_REASON_NOTIFICATION = 1; public static final int PULSE_REASON_SENSOR_SIGMOTION = 2; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java index 38b32e950225..44bb33aee2df 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java @@ -106,6 +106,7 @@ public class DozeMachine { private final ArrayList<State> mQueuedRequests = new ArrayList<>(); private State mState = State.UNINITIALIZED; + private int mPulseReason; private boolean mWakeLockHeldForCurrentState = false; public DozeMachine(Service service, AmbientDisplayConfiguration config, @@ -133,6 +134,20 @@ public class DozeMachine { */ @MainThread public void requestState(State requestedState) { + Preconditions.checkArgument(requestedState != State.DOZE_REQUEST_PULSE); + requestState(requestedState, DozeLog.PULSE_REASON_NONE); + } + + @MainThread + public void requestPulse(int pulseReason) { + // Must not be called during a transition. There's no inherent problem with that, + // but there's currently no need to execute from a transition and it simplifies the + // code to not have to worry about keeping the pulseReason in mQueuedRequests. + Preconditions.checkState(!isExecutingTransition()); + requestState(State.DOZE_REQUEST_PULSE, pulseReason); + } + + private void requestState(State requestedState, int pulseReason) { Assert.isMainThread(); if (DEBUG) { Log.i(TAG, "request: current=" + mState + " req=" + requestedState, @@ -146,7 +161,7 @@ public class DozeMachine { for (int i = 0; i < mQueuedRequests.size(); i++) { // Transitions in Parts can call back into requestState, which will // cause mQueuedRequests to grow. - transitionTo(mQueuedRequests.get(i)); + transitionTo(mQueuedRequests.get(i), pulseReason); } mQueuedRequests.clear(); mWakeLock.release(); @@ -165,6 +180,20 @@ public class DozeMachine { return mState; } + /** + * @return the current pulse reason. + * + * This is only valid if the machine is currently in one of the pulse states. + */ + @MainThread + public int getPulseReason() { + Assert.isMainThread(); + Preconditions.checkState(mState == State.DOZE_REQUEST_PULSE + || mState == State.DOZE_PULSING + || mState == State.DOZE_PULSE_DONE, "must be in pulsing state, but is " + mState); + return mPulseReason; + } + /** Requests the PowerManager to wake up now. */ public void wakeUp() { mDozeService.requestWakeUp(); @@ -174,7 +203,7 @@ public class DozeMachine { return !mQueuedRequests.isEmpty(); } - private void transitionTo(State requestedState) { + private void transitionTo(State requestedState, int pulseReason) { State newState = transitionPolicy(requestedState); if (DEBUG) { @@ -190,6 +219,7 @@ public class DozeMachine { State oldState = mState; mState = newState; + updatePulseReason(newState, oldState, pulseReason); performTransitionOnComponents(oldState, newState); updateScreenState(newState); updateWakeLockState(newState); @@ -197,6 +227,14 @@ public class DozeMachine { resolveIntermediateState(newState); } + private void updatePulseReason(State newState, State oldState, int pulseReason) { + if (newState == State.DOZE_REQUEST_PULSE) { + mPulseReason = pulseReason; + } else if (oldState == State.DOZE_PULSE_DONE) { + mPulseReason = DozeLog.PULSE_REASON_NONE; + } + } + private void performTransitionOnComponents(State oldState, State newState) { for (Part p : mParts) { p.transitionTo(oldState, newState); @@ -280,7 +318,8 @@ public class DozeMachine { case INITIALIZED: case DOZE_PULSE_DONE: transitionTo(mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT) - ? DozeMachine.State.DOZE_AOD : DozeMachine.State.DOZE); + ? DozeMachine.State.DOZE_AOD : DozeMachine.State.DOZE, + DozeLog.PULSE_REASON_NONE); break; default: break; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 2096956e8c8f..563b8fe35401 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -227,7 +227,7 @@ public class DozeTriggers implements DozeMachine.Part { mDozeHost.isPulsingBlocked()); return; } - mMachine.requestState(DozeMachine.State.DOZE_REQUEST_PULSE); + mMachine.requestPulse(reason); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index 03076cc4b7a8..64a152e1ac15 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -83,7 +83,7 @@ public class DozeUi implements DozeMachine.Part { unscheduleTimeTick(); break; case DOZE_REQUEST_PULSE: - pulseWhileDozing(DozeLog.PULSE_REASON_NOTIFICATION /* TODO */); + pulseWhileDozing(mMachine.getPulseReason()); break; case DOZE_PULSE_DONE: mHost.abortPulsing(); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index df03fdc46d06..bdc08718d37f 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -107,12 +107,12 @@ public class PipManager implements BasePipManager { } @Override - public void onPinnedActivityRestartAttempt() { + public void onPinnedActivityRestartAttempt(boolean clearedTask) { if (!checkCurrentUserId(false /* debug */)) { return; } - mTouchHandler.getMotionHelper().expandPip(); + mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */); } }; diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index fc52a2ec5994..5121c8d0a375 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -140,14 +140,25 @@ public class PipMotionHelper { * Resizes the pinned stack back to fullscreen. */ void expandPip() { + expandPip(false /* skipAnimation */); + } + + /** + * Resizes the pinned stack back to fullscreen. + */ + void expandPip(boolean skipAnimation) { cancelAnimations(); mHandler.post(() -> { try { - mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */, - true /* allowResizeInDockedMode */, true /* preserveWindows */, - true /* animate */, EXPAND_STACK_TO_FULLSCREEN_DURATION); + if (skipAnimation) { + mActivityManager.moveTasksToFullscreenStack(PINNED_STACK_ID, true /* onTop */); + } else { + mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */, + true /* allowResizeInDockedMode */, true /* preserveWindows */, + true /* animate */, EXPAND_STACK_TO_FULLSCREEN_DURATION); + } } catch (RemoteException e) { - Log.e(TAG, "Error showing PiP menu activity", e); + Log.e(TAG, "Error expanding PiP activity", e); } }); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 657f08be8b52..9735bfc666d7 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -625,7 +625,7 @@ public class PipManager implements BasePipManager { } @Override - public void onPinnedActivityRestartAttempt() { + public void onPinnedActivityRestartAttempt(boolean clearedTask) { if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()"); if (!checkCurrentUserId(DEBUG)) { return; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index f5e096ebbbef..c4d88aec7b8c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -22,6 +22,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.metrics.LogMaker; import android.os.Handler; import android.os.Message; import android.service.quicksettings.Tile; @@ -30,6 +31,7 @@ import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settingslib.Utils; @@ -179,7 +181,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback { public void openDetails(String subPanel) { QSTile tile = getTile(subPanel); - showDetailAdapter(true, tile.getDetailAdapter(), new int[] {getWidth() / 2, 0}); + showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0}); } private QSTile getTile(String subPanel) { @@ -485,8 +487,9 @@ public class QSPanel extends LinearLayout implements Tunable, Callback { private void logTiles() { for (int i = 0; i < mRecords.size(); i++) { - TileRecord tileRecord = mRecords.get(i); - mMetricsLogger.visible(tileRecord.tile.getMetricsCategory()); + QSTile tile = mRecords.get(i).tile; + mMetricsLogger.write(tile.populate(new LogMaker(tile.getMetricsCategory()) + .setType(MetricsEvent.TYPE_OPEN))); } } @@ -544,12 +547,13 @@ public class QSPanel extends LinearLayout implements Tunable, Callback { private static final int SHOW_DETAIL = 1; private static final int SET_TILE_VISIBILITY = 2; private static final int ANNOUNCE_FOR_ACCESSIBILITY = 3; + @Override public void handleMessage(Message msg) { if (msg.what == SHOW_DETAIL) { - handleShowDetail((Record)msg.obj, msg.arg1 != 0); + handleShowDetail((Record) msg.obj, msg.arg1 != 0); } else if (msg.what == ANNOUNCE_FOR_ACCESSIBILITY) { - announceForAccessibility((CharSequence)msg.obj); + announceForAccessibility((CharSequence) msg.obj); } } } @@ -569,8 +573,11 @@ public class QSPanel extends LinearLayout implements Tunable, Callback { public interface QSTileLayout { void addTile(TileRecord tile); + void removeTile(TileRecord tile); + int getOffsetTop(TileRecord tile); + boolean updateResources(); void setListening(boolean listening); diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index dc9176f66bb2..017365f6c64d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -157,7 +157,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener } @Override - protected LogMaker populate(LogMaker logMaker) { + public LogMaker populate(LogMaker logMaker) { return super.populate(logMaker).setComponentName(mComponent); } @@ -275,7 +275,6 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener } catch (RemoteException e) { // Called through wrapper, won't happen here. } - MetricsLogger.action(mContext, getMetricsCategory(), mComponent.getPackageName()); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index 976efb2c289b..32af2305062e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -175,7 +175,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile { mHandler.sendEmptyMessage(H.LONG_CLICK); } - protected LogMaker populate(LogMaker logMaker) { + public LogMaker populate(LogMaker logMaker) { if (mState instanceof BooleanState) { logMaker.addTaggedData(FIELD_QS_VALUE, ((BooleanState) mState).value ? 1 : 0); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index a9e1f61b7d47..f431517cfe77 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -158,7 +158,7 @@ public class SystemServicesProxy { public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { } public void onActivityPinned(String packageName) { } public void onActivityUnpinned() { } - public void onPinnedActivityRestartAttempt() { } + public void onPinnedActivityRestartAttempt(boolean clearedTask) { } public void onPinnedStackAnimationStarted() { } public void onPinnedStackAnimationEnded() { } public void onActivityForcedResizable(String packageName, int taskId, int reason) { } @@ -223,10 +223,11 @@ public class SystemServicesProxy { } @Override - public void onPinnedActivityRestartAttempt() + public void onPinnedActivityRestartAttempt(boolean clearedTask) throws RemoteException{ mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT); - mHandler.sendEmptyMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT); + mHandler.obtainMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT, clearedTask ? 1 : 0, 0) + .sendToTarget(); } @Override @@ -1294,7 +1295,8 @@ public class SystemServicesProxy { } case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: { for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onPinnedActivityRestartAttempt(); + mTaskStackListeners.get(i).onPinnedActivityRestartAttempt( + msg.arg1 != 0); } break; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java index 5817e9252c73..36be49deef35 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java @@ -279,6 +279,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { } MetricsLogger.action(mSv.getContext(), MetricsEvent.OVERVIEW_SCROLL); + mLastY = mDownY = y; } } if (mIsScrolling) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java index cdbde5e8b0e1..d20360234c74 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java @@ -106,7 +106,7 @@ public class DozeMachineTest { public void testPulseDone_goesToDoze() { when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(false); mMachine.requestState(INITIALIZED); - mMachine.requestState(DOZE_REQUEST_PULSE); + mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSING); mMachine.requestState(DOZE_PULSE_DONE); @@ -119,7 +119,7 @@ public class DozeMachineTest { public void testPulseDone_goesToAoD() { when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true); mMachine.requestState(INITIALIZED); - mMachine.requestState(DOZE_REQUEST_PULSE); + mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSING); mMachine.requestState(DOZE_PULSE_DONE); @@ -163,7 +163,7 @@ public class DozeMachineTest { public void testWakeLock_heldInPulseStates() { mMachine.requestState(INITIALIZED); - mMachine.requestState(DOZE_REQUEST_PULSE); + mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); assertTrue(mWakeLockFake.isHeld()); mMachine.requestState(DOZE_PULSING); @@ -186,7 +186,7 @@ public class DozeMachineTest { mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE); - mMachine.requestState(DOZE_REQUEST_PULSE); + mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSING); mMachine.requestState(DOZE_PULSE_DONE); @@ -198,9 +198,9 @@ public class DozeMachineTest { mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE); - mMachine.requestState(DOZE_REQUEST_PULSE); + mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSING); - mMachine.requestState(DOZE_REQUEST_PULSE); + mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSE_DONE); } @@ -209,7 +209,7 @@ public class DozeMachineTest { mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE); - mMachine.requestState(DOZE_REQUEST_PULSE); + mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSE_DONE); } @@ -235,7 +235,7 @@ public class DozeMachineTest { public void testScreen_onInPulse() { mMachine.requestState(INITIALIZED); - mMachine.requestState(DOZE_REQUEST_PULSE); + mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSING); assertEquals(Display.STATE_DOZE, mServiceFake.screenState); @@ -246,7 +246,7 @@ public class DozeMachineTest { mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE); - mMachine.requestState(DOZE_REQUEST_PULSE); + mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); assertEquals(Display.STATE_OFF, mServiceFake.screenState); } @@ -256,7 +256,7 @@ public class DozeMachineTest { mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE_AOD); - mMachine.requestState(DOZE_REQUEST_PULSE); + mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); assertEquals(Display.STATE_DOZE, mServiceFake.screenState); } @@ -270,12 +270,43 @@ public class DozeMachineTest { return null; }).when(mPartMock).transitionTo(any(), eq(DOZE_REQUEST_PULSE)); - mMachine.requestState(DOZE_REQUEST_PULSE); + mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); assertEquals(DOZE_PULSING, mMachine.getState()); } @Test + public void testPulseReason_getMatchesRequest() { + mMachine.requestState(INITIALIZED); + mMachine.requestState(DOZE); + mMachine.requestPulse(DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP); + + assertEquals(DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP, mMachine.getPulseReason()); + } + + @Test + public void testPulseReason_getFromTransition() { + mMachine.requestState(INITIALIZED); + mMachine.requestState(DOZE); + doAnswer(inv -> { + DozeMachine.State newState = inv.getArgument(1); + if (newState == DOZE_REQUEST_PULSE + || newState == DOZE_PULSING + || newState == DOZE_PULSE_DONE) { + assertEquals(DozeLog.PULSE_REASON_NOTIFICATION, mMachine.getPulseReason()); + } else { + assertTrue("unexpected state " + newState, + newState == DOZE || newState == DOZE_AOD); + } + return null; + }).when(mPartMock).transitionTo(any(), any()); + + mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); + mMachine.requestState(DOZE_PULSING); + mMachine.requestState(DOZE_PULSE_DONE); + } + + @Test public void testWakeUp_wakesUp() { mMachine.wakeUp(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 12e75a14ba2b..8934460c380e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -22,26 +22,28 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.mockito.Mockito.withSettings; import android.app.Instrumentation; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.wakelock.WakeLock; import com.android.systemui.util.wakelock.WakeLockFake; +import com.android.systemui.utils.hardware.FakeSensorManager; import org.junit.Before; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; -import org.mockito.Answers; -import org.mockito.MockSettings; +import org.junit.runner.RunWith; +@SmallTest +@RunWith(AndroidJUnit4.class) public class DozeTriggersTest { private Context mContext; private DozeTriggers mTriggers; @@ -49,7 +51,7 @@ public class DozeTriggersTest { private DozeHostFake mHost; private AmbientDisplayConfiguration mConfig; private DozeParameters mParameters; - private SensorManagerFake mSensors; + private FakeSensorManager mSensors; private Handler mHandler; private WakeLock mWakeLock; private Instrumentation mInstrumentation; @@ -68,7 +70,7 @@ public class DozeTriggersTest { mHost = new DozeHostFake(); mConfig = DozeConfigurationUtil.createMockConfig(); mParameters = DozeConfigurationUtil.createMockParameters(); - mSensors = new SensorManagerFake(mContext); + mSensors = new FakeSensorManager(mContext); mHandler = new Handler(Looper.getMainLooper()); mWakeLock = new WakeLockFake(); @@ -79,32 +81,32 @@ public class DozeTriggersTest { } @Test - @Ignore("setup crashes on virtual devices") public void testOnNotification_stillWorksAfterOneFailedProxCheck() throws Exception { when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); mInstrumentation.runOnMainSync(()->{ mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED); - mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.DOZE); + mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); mHost.callback.onNotificationHeadsUp(); }); mInstrumentation.runOnMainSync(() -> { - mSensors.PROXIMITY.sendProximityResult(false); /* Near */ + mSensors.getMockProximitySensor().sendProximityResult(false); /* Near */ }); verify(mMachine, never()).requestState(any()); + verify(mMachine, never()).requestPulse(anyInt()); mInstrumentation.runOnMainSync(()->{ mHost.callback.onNotificationHeadsUp(); }); mInstrumentation.runOnMainSync(() -> { - mSensors.PROXIMITY.sendProximityResult(true); /* Far */ + mSensors.getMockProximitySensor().sendProximityResult(true); /* Far */ }); - verify(mMachine).requestState(DozeMachine.State.DOZE_REQUEST_PULSE); + verify(mMachine).requestPulse(anyInt()); } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/SensorManagerFake.java b/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java index 5b4b8917d8d1..30be6658d4b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/SensorManagerFake.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java @@ -11,10 +11,10 @@ * 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. + * limitations under the License */ -package com.android.systemui.doze; +package com.android.systemui.utils.hardware; import android.content.Context; import android.hardware.HardwareBuffer; @@ -33,6 +33,8 @@ import android.util.ArraySet; import com.google.android.collect.Lists; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; @@ -44,18 +46,38 @@ import java.util.List; * Note that this class ignores the "Handler" argument, so the test is responsible for calling the * listener on the right thread. */ -public class SensorManagerFake extends SensorManager { +public class FakeSensorManager extends SensorManager { - public MockSensor PROXIMITY; + private final MockProximitySensor mMockProximitySensor; - public SensorManagerFake(Context context) { - PROXIMITY = new MockSensor(context.getSystemService(SensorManager.class) - .getDefaultSensor(Sensor.TYPE_PROXIMITY)); + public FakeSensorManager(Context context) throws Exception { + Sensor proxSensor = context.getSystemService(SensorManager.class) + .getDefaultSensor(Sensor.TYPE_PROXIMITY); + if (proxSensor == null) { + // No prox? Let's create a fake one! + proxSensor = createSensor(Sensor.TYPE_PROXIMITY); + } + mMockProximitySensor = new MockProximitySensor(proxSensor); + } + + public MockProximitySensor getMockProximitySensor() { + return mMockProximitySensor; + } + + @Override + public Sensor getDefaultSensor(int type) { + Sensor s = super.getDefaultSensor(type); + if (s != null) { + return s; + } + // Our mock sensors aren't wakeup, and it's a pain to create them that way. Instead, just + // return non-wakeup sensors if we can't find a wakeup sensor. + return getDefaultSensor(type, false /* wakeup */); } @Override protected List<Sensor> getFullSensorList() { - return Lists.newArrayList(PROXIMITY.sensor); + return Lists.newArrayList(mMockProximitySensor.sensor); } @Override @@ -65,8 +87,8 @@ public class SensorManagerFake extends SensorManager { @Override protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) { - if (sensor == PROXIMITY.sensor || sensor == null) { - PROXIMITY.listeners.remove(listener); + if (sensor == mMockProximitySensor.sensor || sensor == null) { + mMockProximitySensor.listeners.remove(listener); } } @@ -74,8 +96,8 @@ public class SensorManagerFake extends SensorManager { protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor, int delayUs, Handler handler, int maxReportLatencyUs, int reservedFlags) { - if (sensor == PROXIMITY.sensor) { - PROXIMITY.listeners.add(listener); + if (sensor == mMockProximitySensor.sensor) { + mMockProximitySensor.listeners.add(listener); return true; } return false; @@ -141,11 +163,44 @@ public class SensorManagerFake extends SensorManager { return false; } - public class MockSensor { + private Sensor createSensor(int type) throws Exception { + Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor(); + constr.setAccessible(true); + Sensor sensor = constr.newInstance(); + + setSensorType(sensor, type); + setSensorField(sensor, "mName", "Mock " + sensor.getStringType() + "/" + type); + setSensorField(sensor, "mVendor", "Mock Vendor"); + setSensorField(sensor, "mVersion", 1); + setSensorField(sensor, "mHandle", -1); + setSensorField(sensor, "mMaxRange", 10); + setSensorField(sensor, "mResolution", 1); + setSensorField(sensor, "mPower", 1); + setSensorField(sensor, "mMinDelay", 1000); + setSensorField(sensor, "mMaxDelay", 1000000000); + setSensorField(sensor, "mFlags", 0); + setSensorField(sensor, "mId", -1); + + return sensor; + } + + private void setSensorField(Sensor sensor, String fieldName, Object value) throws Exception { + Field field = Sensor.class.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(sensor, value); + } + + private void setSensorType(Sensor sensor, int type) throws Exception { + Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE); + setter.setAccessible(true); + setter.invoke(sensor, type); + } + + public class MockProximitySensor { final Sensor sensor; final ArraySet<SensorEventListener> listeners = new ArraySet<>(); - private MockSensor(Sensor sensor) { + private MockProximitySensor(Sensor sensor) { this.sensor = sensor; } diff --git a/preloaded-classes b/preloaded-classes index 892c59394855..493e9803d81e 100644 --- a/preloaded-classes +++ b/preloaded-classes @@ -1820,6 +1820,7 @@ android.text.FontConfig$Font$1 android.text.GetChars android.text.GraphicsOperations android.text.Html +android.text.Html$HtmlParser android.text.Hyphenator android.text.InputFilter android.text.InputType diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java index 68ade63ac516..cbf97ddc8abb 100644 --- a/services/autofill/java/com/android/server/autofill/Helper.java +++ b/services/autofill/java/com/android/server/autofill/Helper.java @@ -20,7 +20,9 @@ import android.annotation.NonNull; import android.app.assist.AssistStructure; import android.app.assist.AssistStructure.ViewNode; import android.os.Bundle; +import android.util.DebugUtils; import android.view.autofill.AutofillId; +import android.view.autofill.AutofillManager; import java.util.Arrays; import java.util.Objects; @@ -40,6 +42,10 @@ public final class Helper { */ public static boolean sVerbose = false; + private Helper() { + throw new UnsupportedOperationException("contains static members only"); + } + static void append(StringBuilder builder, Bundle bundle) { if (bundle == null || !sVerbose) { builder.append("null"); @@ -62,8 +68,8 @@ public final class Helper { return builder.toString(); } - private Helper() { - throw new UnsupportedOperationException("contains static members only"); + static String getUpdateActionAsString(int action) { + return DebugUtils.flagsToString(AutofillManager.class, "ACTION_", action); } static ViewNode findViewNodeById(@NonNull AssistStructure structure, @NonNull AutofillId id) { diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 018fb6886cd9..23b734adaa9a 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -25,9 +25,13 @@ import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED; import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED; import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED; +import static com.android.server.autofill.Helper.findViewNodeById; +import static com.android.server.autofill.Helper.getUpdateActionAsString; import static com.android.server.autofill.Helper.sDebug; import static com.android.server.autofill.Helper.sVerbose; -import static com.android.server.autofill.Helper.findViewNodeById; +import static com.android.server.autofill.ViewState.STATE_AUTOFILLED; +import static com.android.server.autofill.ViewState.STATE_FILLABLE; +import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION; import android.annotation.NonNull; import android.annotation.Nullable; @@ -221,7 +225,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int numContexts = mContexts.size(); for (int i = 0; i < numContexts; i++) { - fillStructureWithAllowedValues(mContexts.get(i).getStructure()); + fillStructureWithAllowedValues(mContexts.get(i).getStructure(), flags); } request = new FillRequest(requestId, mContexts, mClientState, flags); @@ -235,10 +239,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Updates values of the nodes in the structure so that: * - proper node is focused * - autofillValue is sent back to service when it was previously autofilled + * - autofillValue is sent in the view used to force a request * * @param structure The structure to be filled + * @param flags The flags that started the session */ - private void fillStructureWithAllowedValues(@NonNull AssistStructure structure) { + private void fillStructureWithAllowedValues(@NonNull AssistStructure structure, int flags) { final int numViewStates = mViewStates.size(); for (int i = 0; i < numViewStates; i++) { final ViewState viewState = mViewStates.valueAt(i); @@ -249,16 +255,23 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState continue; } - final AutofillValue initialValue = viewState.getInitialValue(); + final AutofillValue currentValue = viewState.getCurrentValue(); final AutofillValue filledValue = viewState.getAutofilledValue(); final AutofillOverlay overlay = new AutofillOverlay(); - if (filledValue != null && !filledValue.equals(initialValue)) { - overlay.value = filledValue; + + // Sanitizes the value if the current value matches what the service sent. + if (filledValue != null && filledValue.equals(currentValue)) { + overlay.value = currentValue; } + if (mCurrentViewId != null) { + // Updates the focus value. overlay.focused = mCurrentViewId.equals(viewState.id); + // Sanitizes the value of the focused field in a manual request. + if (overlay.focused && (flags & FLAG_MANUAL_REQUEST) != 0) { + overlay.value = currentValue; + } } - node.setAutofillOverlay(overlay); } } @@ -883,6 +896,40 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** + * Starts (if necessary) a new fill request upon entering a view. + * + * <p>A new request will be started in 2 scenarios: + * <ol> + * <li>If the user manually requested autofill after the view was already filled. + * <li>If the view is part of a new partition. + * </ol> + * + * @param id The id of the view that is entered. + * @param viewState The view that is entered. + * @param flags The flag that was passed by the AutofillManager. + */ + private void requestNewFillResponseIfNecessaryLocked(@NonNull AutofillId id, + @NonNull ViewState viewState, int flags) { + // First check if this is a manual request after view was autofilled. + final int state = viewState.getState(); + final boolean restart = (state & STATE_AUTOFILLED) != 0 + && (flags & FLAG_MANUAL_REQUEST) != 0; + if (restart) { + if (sDebug) Slog.d(TAG, "Re-starting session on view " + id); + viewState.setState(STATE_RESTARTED_SESSION); + requestNewFillResponseLocked(flags); + return; + } + + // If it's not, then check if it it should start a partition. + if (shouldStartNewPartitionLocked(id)) { + if (sDebug) Slog.d(TAG, "Starting partition for view id " + id); + viewState.setState(ViewState.STATE_STARTED_PARTITION); + requestNewFillResponseLocked(flags); + } + } + + /** * Determines if a new partition should be started for an id. * * @param id The id of the view that is entered @@ -938,6 +985,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + id + " destroyed"); return; } + if (sVerbose) { + Slog.v(TAG, "updateLocked(): id=" + id + ", action=" + getUpdateActionAsString(action) + + ", flags=" + flags); + } ViewState viewState = mViewStates.get(id); if (viewState == null) { @@ -992,13 +1043,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } break; case ACTION_VIEW_ENTERED: - if (shouldStartNewPartitionLocked(id)) { - if (sDebug) { - Slog.d(TAG, "Starting partition for view id " + viewState.id); - } - viewState.setState(ViewState.STATE_STARTED_PARTITION); - requestNewFillResponseLocked(flags); - } + requestNewFillResponseIfNecessaryLocked(id, viewState, flags); // Remove the UI if the ViewState has changed. if (mCurrentViewId != viewState.id) { diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java index d114e1452373..561c603ea3b0 100644 --- a/services/autofill/java/com/android/server/autofill/ViewState.java +++ b/services/autofill/java/com/android/server/autofill/ViewState.java @@ -47,23 +47,25 @@ final class ViewState { private static final String TAG = "ViewState"; // NOTE: state constants must be public because of flagstoString(). - public static final int STATE_UNKNOWN = 0x00; + public static final int STATE_UNKNOWN = 0x000; /** Initial state. */ - public static final int STATE_INITIAL = 0x01; + public static final int STATE_INITIAL = 0x001; /** View id is present in a dataset returned by the service. */ - public static final int STATE_FILLABLE = 0x02; + public static final int STATE_FILLABLE = 0x002; /** View was autofilled after user selected a dataset. */ - public static final int STATE_AUTOFILLED = 0x04; + public static final int STATE_AUTOFILLED = 0x004; /** View value was changed, but not by the service. */ - public static final int STATE_CHANGED = 0x08; + public static final int STATE_CHANGED = 0x008; /** Set only in the View that started a session. */ - public static final int STATE_STARTED_SESSION = 0x10; + public static final int STATE_STARTED_SESSION = 0x010; /** View that started a new partition when focused on. */ - public static final int STATE_STARTED_PARTITION = 0x20; + public static final int STATE_STARTED_PARTITION = 0x020; /** User select a dataset in this view, but service must authenticate first. */ - public static final int STATE_WAITING_DATASET_AUTH = 0x40; + public static final int STATE_WAITING_DATASET_AUTH = 0x040; /** Service does not care about this view. */ - public static final int STATE_IGNORED = 0x80; + public static final int STATE_IGNORED = 0x080; + /** User manually request autofill in this view, after it was already autofilled. */ + public static final int STATE_RESTARTED_SESSION = 0x100; public final AutofillId id; diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index dc2dfa8ff289..a68a7ddd6f9c 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -2387,7 +2387,7 @@ public class BackupManagerService { long token = mAncestralToken; synchronized (mQueueLock) { - if (mEverStoredApps.contains(packageName)) { + if (mCurrentToken != 0 && mEverStoredApps.contains(packageName)) { if (MORE_DEBUG) { Slog.i(TAG, "App in ever-stored, so using current token"); } @@ -10464,8 +10464,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF final long oldId = Binder.clearCallingIdentity(); try { String prevTransport = mTransportManager.selectTransport(transport); - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.BACKUP_TRANSPORT, transport); + updateStateForTransport(transport); Slog.v(TAG, "selectBackupTransport() set " + mTransportManager.getCurrentTransportName() + " returning " + prevTransport); return prevTransport; @@ -10488,9 +10487,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF @Override public void onSuccess(String transportName) { mTransportManager.selectTransport(transportName); - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.BACKUP_TRANSPORT, - mTransportManager.getCurrentTransportName()); + updateStateForTransport(mTransportManager.getCurrentTransportName()); Slog.v(TAG, "Transport successfully selected: " + transport.flattenToShortString()); try { listener.onSuccess(transportName); @@ -10513,6 +10510,28 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF Binder.restoreCallingIdentity(oldId); } + private void updateStateForTransport(String newTransportName) { + // Publish the name change + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.BACKUP_TRANSPORT, newTransportName); + + // And update our current-dataset bookkeeping + IBackupTransport transport = mTransportManager.getTransportBinder(newTransportName); + if (transport != null) { + try { + mCurrentToken = transport.getCurrentRestoreSet(); + } catch (Exception e) { + // Oops. We can't know the current dataset token, so reset and figure it out + // when we do the next k/v backup operation on this transport. + mCurrentToken = 0; + } + } else { + // The named transport isn't bound at this particular moment, so we can't + // know yet what its current dataset token is. Reset as above. + mCurrentToken = 0; + } + } + // Supply the configuration Intent for the given transport. If the name is not one // of the available transports, or if the transport does not supply any configuration // UI, the method returns null. diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java index 2c14a7cd030e..aafa88a8eae5 100644 --- a/services/backup/java/com/android/server/backup/Trampoline.java +++ b/services/backup/java/com/android/server/backup/Trampoline.java @@ -304,6 +304,14 @@ public class Trampoline extends IBackupManager.Stub { BackupManagerService svc = mService; if (svc != null) { svc.selectBackupTransportAsync(transport, listener); + } else { + if (listener != null) { + try { + listener.onFailure(BackupManager.ERROR_BACKUP_NOT_ALLOWED); + } catch (RemoteException ex) { + // Ignore + } + } } } diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java index 67f105e503d3..da1f32c9d203 100644 --- a/services/backup/java/com/android/server/backup/TransportManager.java +++ b/services/backup/java/com/android/server/backup/TransportManager.java @@ -182,13 +182,13 @@ class TransportManager { String[] getBoundTransportNames() { synchronized (mTransportLock) { - return mBoundTransports.keySet().toArray(new String[0]); + return mBoundTransports.keySet().toArray(new String[mBoundTransports.size()]); } } ComponentName[] getAllTransportCompenents() { synchronized (mTransportLock) { - return mValidTransports.keySet().toArray(new ComponentName[0]); + return mValidTransports.keySet().toArray(new ComponentName[mValidTransports.size()]); } } diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 05ffcf071268..550774a1df58 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -1049,6 +1049,7 @@ class AlarmManagerService extends SystemService { if (timeZoneWasChanged) { Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING + | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); intent.putExtra("time-zone", zone.getID()); getContext().sendBroadcastAsUser(intent, UserHandle.ALL); diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java index 114d7619a024..fdc0bba09a52 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -631,6 +631,11 @@ public class NetworkScoreService extends INetworkScoreService.Stub { PackageManager.PERMISSION_GRANTED; } + private boolean callerCanScoreNetworks() { + return mContext.checkCallingOrSelfPermission(permission.SCORE_NETWORKS) == + PackageManager.PERMISSION_GRANTED; + } + @Override public boolean clearScores() { // Only the active scorer or the system should be allowed to flush all scores. @@ -651,9 +656,9 @@ public class NetworkScoreService extends INetworkScoreService.Stub { @Override public boolean setActiveScorer(String packageName) { // Only the system can set the active scorer - if (!isCallerSystemProcess(getCallingUid()) && !callerCanRequestScores()) { + if (!isCallerSystemProcess(getCallingUid()) && !callerCanScoreNetworks()) { throw new SecurityException( - "Caller is neither the system process nor a score requester."); + "Caller is neither the system process or a network scorer."); } return mNetworkScorerAppManager.setActiveScorer(packageName); diff --git a/services/core/java/com/android/server/NetworkScorerAppManager.java b/services/core/java/com/android/server/NetworkScorerAppManager.java index 8404025e6726..42777bf33bd3 100644 --- a/services/core/java/com/android/server/NetworkScorerAppManager.java +++ b/services/core/java/com/android/server/NetworkScorerAppManager.java @@ -208,11 +208,11 @@ public class NetworkScorerAppManager { * * <p>The caller must have permission to write to {@link Settings.Global}. * - * @param packageName the packageName of the new scorer to use. If null, the scoring app will - * revert back to the configured default. Otherwise, the scorer will only - * be set if it is a valid scorer application. - * @return true if the scorer was changed, or false if the package is not a valid scorer or - * a valid network recommendation provider exists. + * @param packageName the packageName of the new scorer to use. If null, scoring will be forced + * off, otherwise the scorer will only be set if it is a valid scorer + * application. + * @return true if the package was a valid scorer (including <code>null</code>) and now + * represents the active scorer, false otherwise. */ @VisibleForTesting public boolean setActiveScorer(String packageName) { @@ -223,16 +223,19 @@ public class NetworkScorerAppManager { return true; } - if (packageName == null) { - // revert to the default setting. - packageName = getDefaultPackageSetting(); + if (TextUtils.isEmpty(packageName)) { + Log.i(TAG, "Network scorer forced off, was: " + oldPackageName); + setNetworkRecommendationsPackage(null); + setNetworkRecommendationsEnabledSetting( + NetworkScoreManager.RECOMMENDATIONS_ENABLED_FORCED_OFF); + return true; } - Log.i(TAG, "Changing network scorer from " + oldPackageName + " to " + packageName); - // We only make the change if the new package is valid. if (getScorer(packageName) != null) { + Log.i(TAG, "Changing network scorer from " + oldPackageName + " to " + packageName); setNetworkRecommendationsPackage(packageName); + setNetworkRecommendationsEnabledSetting(NetworkScoreManager.RECOMMENDATIONS_ENABLED_ON); return true; } else { Log.w(TAG, "Requested network scorer is not valid: " + packageName); diff --git a/services/core/java/com/android/server/vr/CompatibilityDisplay.java b/services/core/java/com/android/server/Vr2dDisplay.java index d7cdf085bfb7..1116e4e2c3fc 100644 --- a/services/core/java/com/android/server/vr/CompatibilityDisplay.java +++ b/services/core/java/com/android/server/Vr2dDisplay.java @@ -3,7 +3,7 @@ package com.android.server.vr; import static android.view.Display.INVALID_DISPLAY; import android.app.ActivityManagerInternal; -import android.app.CompatibilityDisplayProperties; +import android.app.Vr2dDisplayProperties; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; @@ -31,8 +31,8 @@ import com.android.server.vr.VrManagerService; * Creates a 2D Virtual Display while VR Mode is enabled. This display will be used to run and * render 2D app within a VR experience. For example, bringing up the 2D calculator app in VR. */ -class CompatibilityDisplay { - private final static String TAG = "CompatDisplay"; +class Vr2dDisplay { + private final static String TAG = "Vr2dDisplay"; private final static boolean DEBUG = false; // TODO: Go over these values and figure out what is best @@ -42,13 +42,13 @@ class CompatibilityDisplay { private final static int STOP_VIRTUAL_DISPLAY_DELAY_MILLIS = 2000; private final static String DEBUG_ACTION_SET_MODE = - "com.android.server.vr.CompatibilityDisplay.SET_MODE"; + "com.android.server.vr.Vr2dDisplay.SET_MODE"; private final static String DEBUG_EXTRA_MODE_ON = - "com.android.server.vr.CompatibilityDisplay.EXTRA_MODE_ON"; + "com.android.server.vr.Vr2dDisplay.EXTRA_MODE_ON"; private final static String DEBUG_ACTION_SET_SURFACE = - "com.android.server.vr.CompatibilityDisplay.SET_SURFACE"; + "com.android.server.vr.Vr2dDisplay.SET_SURFACE"; private final static String DEBUG_EXTRA_SURFACE = - "com.android.server.vr.CompatibilityDisplay.EXTRA_SURFACE"; + "com.android.server.vr.Vr2dDisplay.EXTRA_SURFACE"; /** * The default width of the VR virtual display @@ -99,7 +99,7 @@ class CompatibilityDisplay { private boolean mIsVrModeOverrideEnabled; private boolean mIsVrModeEnabled; - public CompatibilityDisplay(DisplayManager displayManager, + public Vr2dDisplay(DisplayManager displayManager, ActivityManagerInternal activityManagerInternal, IVrManager vrManager) { mDisplayManager = displayManager; mActivityManagerInternal = activityManagerInternal; @@ -190,7 +190,7 @@ class CompatibilityDisplay { } /** - * Sets the resolution and DPI of the compatibility virtual display used to display + * Sets the resolution and DPI of the Vr2d virtual display used to display * 2D applications in VR mode. * * <p>Requires {@link android.Manifest.permission#ACCESS_VR_MANAGER} permission.</p> @@ -198,7 +198,7 @@ class CompatibilityDisplay { * @param compatDisplayProperties Properties of the virtual display for 2D applications * in VR mode. */ - public void setVirtualDisplayProperties(CompatibilityDisplayProperties compatDisplayProperties) { + public void setVirtualDisplayProperties(Vr2dDisplayProperties compatDisplayProperties) { synchronized(mVdLock) { if (DEBUG) { Log.i(TAG, "VD setVirtualDisplayProperties: res = " + @@ -273,13 +273,13 @@ class CompatibilityDisplay { null /* Surface */, 0 /* flags */); if (mVirtualDisplay != null) { - mActivityManagerInternal.setVrCompatibilityDisplayId( + mActivityManagerInternal.setVr2dDisplayId( mVirtualDisplay.getDisplay().getDisplayId()); // Now create the ImageReader to supply a Surface to the new virtual display. startImageReader(); } else { Log.w(TAG, "Virtual display id is null after createVirtualDisplay"); - mActivityManagerInternal.setVrCompatibilityDisplayId(INVALID_DISPLAY); + mActivityManagerInternal.setVr2dDisplayId(INVALID_DISPLAY); return; } } @@ -302,7 +302,7 @@ class CompatibilityDisplay { } else { Log.i(TAG, "Stopping Virtual Display"); synchronized (mVdLock) { - mActivityManagerInternal.setVrCompatibilityDisplayId(INVALID_DISPLAY); + mActivityManagerInternal.setVr2dDisplayId(INVALID_DISPLAY); setSurfaceLocked(null); // clean up and release the surface first. if (mVirtualDisplay != null) { mVirtualDisplay.release(); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index e0fc531eb326..a5615a9c56fe 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -166,8 +166,11 @@ public final class ActiveServices { */ static final class ActiveForegroundApp { String mPackageName; + int mUid; CharSequence mLabel; boolean mShownWhileScreenOn; + boolean mAppOnTop; + boolean mShownWhileTop; long mStartTime; long mStartVisibleTime; long mEndTime; @@ -728,11 +731,12 @@ public final class ActiveServices { synchronized (mAm) { final long now = SystemClock.elapsedRealtime(); final long nowPlusMin = now + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME; + long nextUpdateTime = Long.MAX_VALUE; if (smap != null) { for (int i = smap.mActiveForegroundApps.size()-1; i >= 0; i--) { ActiveForegroundApp aa = smap.mActiveForegroundApps.valueAt(i); if (aa.mEndTime != 0 && (mScreenOn || aa.mShownWhileScreenOn)) { - if (aa.mEndTime < (aa.mStartVisibleTime + if (!aa.mShownWhileTop && aa.mEndTime < (aa.mStartVisibleTime + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME)) { // Check to see if this should still be displayed... we continue // until it has been shown for at least the timeout duration. @@ -741,6 +745,12 @@ public final class ActiveServices { smap.mActiveForegroundApps.removeAt(i); smap.mActiveForegroundAppsChanged = true; continue; + } else { + long hideTime = aa.mStartVisibleTime + + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME; + if (hideTime < nextUpdateTime) { + nextUpdateTime = hideTime; + } } } else { // This was up for longer than the timeout, so just remove immediately. @@ -749,10 +759,17 @@ public final class ActiveServices { continue; } } - if (active == null) { - active = new ArrayList<>(); + if (!aa.mAppOnTop) { + if (active == null) { + active = new ArrayList<>(); + } + active.add(aa); } - active.add(aa); + } + smap.removeMessages(ServiceMap.MSG_UPDATE_FOREGROUND_APPS); + if (nextUpdateTime < Long.MAX_VALUE) { + Message msg = smap.obtainMessage(); + smap.sendMessageAtTime(msg, nextUpdateTime); } } if (!smap.mActiveForegroundAppsChanged) { @@ -842,7 +859,7 @@ public final class ActiveServices { active.mNumActive--; if (active.mNumActive <= 0) { active.mEndTime = SystemClock.elapsedRealtime(); - if (active.mEndTime >= (active.mStartVisibleTime + if (active.mShownWhileTop || active.mEndTime >= (active.mStartVisibleTime + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME)) { // Have been active for long enough that we will remove it immediately. smap.mActiveForegroundApps.remove(r.packageName); @@ -887,6 +904,31 @@ public final class ActiveServices { } } + void foregroundServiceProcStateChangedLocked(UidRecord uidRec) { + ServiceMap smap = mServiceMap.get(UserHandle.getUserId(uidRec.uid)); + if (smap != null) { + boolean changed = false; + for (int j = smap.mActiveForegroundApps.size()-1; j >= 0; j--) { + ActiveForegroundApp active = smap.mActiveForegroundApps.valueAt(j); + if (active.mUid == uidRec.uid) { + if (uidRec.curProcState <= ActivityManager.PROCESS_STATE_TOP) { + if (!active.mAppOnTop) { + active.mAppOnTop = true; + changed = true; + } + active.mShownWhileTop = true; + } else if (active.mAppOnTop) { + active.mAppOnTop = false; + changed = true; + } + } + } + if (changed) { + requestUpdateActiveForegroundAppsLocked(smap, 0); + } + } + } + private void setServiceForegroundInnerLocked(ServiceRecord r, int id, Notification notification, int flags) { if (id != 0) { @@ -948,7 +990,13 @@ public final class ActiveServices { if (active == null) { active = new ActiveForegroundApp(); active.mPackageName = r.packageName; + active.mUid = r.appInfo.uid; active.mShownWhileScreenOn = mScreenOn; + if (r.app != null) { + active.mAppOnTop = active.mShownWhileTop = + r.app.uidRecord.curProcState + <= ActivityManager.PROCESS_STATE_TOP; + } active.mStartTime = active.mStartVisibleTime = SystemClock.elapsedRealtime(); smap.mActiveForegroundApps.put(r.packageName, active); @@ -1258,6 +1306,10 @@ public final class ActiveServices { mAm.startAssociationLocked(callerApp.uid, callerApp.processName, callerApp.curProcState, s.appInfo.uid, s.name, s.processName); + // Once the apps have become associated, if one of them is caller is ephemeral + // the target app should now be able to see the calling app + mAm.grantEphemeralAccessLocked(callerApp.userId, service, + s.appInfo.uid, UserHandle.getAppId(callerApp.uid)); AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp); ConnectionRecord c = new ConnectionRecord(b, activity, @@ -2786,6 +2838,9 @@ public final class ActiveServices { if (!doit && didSomething) { return true; } + if (doit && filterByClasses == null) { + forceStopPackageLocked(packageName, mServiceMap.valueAt(i).mUserId); + } } } else { ServiceMap smap = mServiceMap.get(userId); @@ -2794,6 +2849,9 @@ public final class ActiveServices { didSomething = collectPackageServicesLocked(packageName, filterByClasses, evenPersistent, doit, killProcess, items); } + if (doit && filterByClasses == null) { + forceStopPackageLocked(packageName, userId); + } } if (mTmpCollectionResults != null) { @@ -2802,10 +2860,11 @@ public final class ActiveServices { } mTmpCollectionResults.clear(); } + return didSomething; } - void removeUninstalledPackageLocked(String packageName, int userId) { + void forceStopPackageLocked(String packageName, int userId) { ServiceMap smap = mServiceMap.get(userId); if (smap != null && smap.mActiveForegroundApps.size() > 0) { for (int i = smap.mActiveForegroundApps.size()-1; i >= 0; i--) { @@ -3636,6 +3695,10 @@ public final class ActiveServices { } pw.print(" mNumActive="); pw.print(aa.mNumActive); + pw.print(" mAppOnTop="); + pw.print(aa.mAppOnTop); + pw.print(" mShownWhileTop="); + pw.print(aa.mShownWhileTop); pw.print(" mShownWhileScreenOn="); pw.println(aa.mShownWhileScreenOn); pw.print(" mStartTime="); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d46a24bec6cb..c65aac4eb2d1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -120,7 +120,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER; @@ -572,11 +571,10 @@ public class ActivityManagerService extends IActivityManager.Stub // Determines whether to take full screen screenshots static final boolean TAKE_FULLSCREEN_SCREENSHOTS = true; - // STOPSHIP: Update default to a smaller value. /** * Default value for {@link Settings.Global#NETWORK_ACCESS_TIMEOUT_MS}. */ - private static final long NETWORK_ACCESS_TIMEOUT_DEFAULT_MS = 2000; // 2 sec + private static final long NETWORK_ACCESS_TIMEOUT_DEFAULT_MS = 200; // 0.2 sec /** * State indicating that there is no need for any blocking for network. @@ -627,8 +625,8 @@ public class ActivityManagerService extends IActivityManager.Stub private final VrController mVrController; - // VR Compatibility Display Id. - int mVrCompatibilityDisplayId = INVALID_DISPLAY; + // VR Vr2d Display Id. + int mVr2dDisplayId = INVALID_DISPLAY; // Whether we should use SCHED_FIFO for UI and RenderThreads. private boolean mUseFifoUiScheduling = false; @@ -4332,6 +4330,7 @@ public class ActivityManagerService extends IActivityManager.Stub validateUid.idle = false; } validateUid.curProcState = validateUid.setProcState = item.processState; + validateUid.lastDispatchedProcStateSeq = item.procStateSeq; } } } @@ -19007,7 +19006,7 @@ public class ActivityManagerService extends IActivityManager.Stub removeTasksByPackageNameLocked(ssp, userId); - mServices.removeUninstalledPackageLocked(ssp, userId); + mServices.forceStopPackageLocked(ssp, userId); // Hide the "unsupported display" dialog if necessary. if (mUnsupportedDisplaySizeDialog != null && ssp.equals( @@ -19890,8 +19889,9 @@ public class ActivityManagerService extends IActivityManager.Stub } /** - * NOTE: For the pinned stack, this method is only called after the bounds animation has - * animated the stack to the fullscreen. + * NOTE: For the pinned stack, this method is usually called after the bounds animation has + * animated the stack to the fullscreen, but can also be called if we are relaunching an + * activity and clearing the task at the same time. */ @Override public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) { @@ -22277,6 +22277,9 @@ public class ActivityManagerService extends IActivityManager.Stub if (uidRec.curProcState > app.curProcState) { uidRec.curProcState = app.curProcState; } + if (app.foregroundServices) { + uidRec.foregroundServices = true; + } } } @@ -22518,6 +22521,9 @@ public class ActivityManagerService extends IActivityManager.Stub uidRec.setWhitelist = uidRec.curWhitelist; enqueueUidChangeLocked(uidRec, -1, uidChange); noteUidProcessState(uidRec.uid, uidRec.curProcState); + if (uidRec.foregroundServices) { + mServices.foregroundServiceProcStateChangedLocked(uidRec); + } } } if (mLocalPowerManager != null) { @@ -23726,17 +23732,17 @@ public class ActivityManagerService extends IActivityManager.Stub /** * Called after virtual display Id is updated by - * {@link com.android.server.vr.CompatibilityDisplay} with a specific - * {@param vrCompatibilityDisplayId}. + * {@link com.android.server.vr.Vr2dDisplay} with a specific + * {@param vrVr2dDisplayId}. */ @Override - public void setVrCompatibilityDisplayId(int vrCompatibilityDisplayId) { + public void setVr2dDisplayId(int vr2dDisplayId) { if (DEBUG_STACK) { - Slog.d(TAG, "setVrCompatibilityDisplayId called for: " + - vrCompatibilityDisplayId); + Slog.d(TAG, "setVr2dDisplayId called for: " + + vr2dDisplayId); } synchronized (ActivityManagerService.this) { - mVrCompatibilityDisplayId = vrCompatibilityDisplayId; + mVr2dDisplayId = vr2dDisplayId; } } } @@ -23800,11 +23806,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (totalTime >= mWaitForNetworkTimeoutMs) { Slog.wtf(TAG_NETWORK, "Total time waited for network rules to get updated: " + totalTime + ". Uid: " + callingUid + " procStateSeq: " - + procStateSeq); - } else if (DEBUG_NETWORK || totalTime >= mWaitForNetworkTimeoutMs / 2) { - Slog.d(TAG_NETWORK, "Total time waited for network rules to get updated: " - + totalTime + ". Uid: " + callingUid + " procStateSeq: " - + procStateSeq); + + procStateSeq + " UidRec: " + record + + " validateUidRec: " + mValidateUids.get(callingUid)); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 19b9b452df7e..1f1aa8e8cdd9 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -186,7 +186,7 @@ class ActivityStarter { private IVoiceInteractionSession mVoiceSession; private IVoiceInteractor mVoiceInteractor; - private boolean mUsingVrCompatibilityDisplay; + private boolean mUsingVr2dDisplay; private void reset() { mStartActivity = null; @@ -226,14 +226,14 @@ class ActivityStarter { mVoiceSession = null; mVoiceInteractor = null; - mUsingVrCompatibilityDisplay = false; + mUsingVr2dDisplay = false; } ActivityStarter(ActivityManagerService service, ActivityStackSupervisor supervisor) { mService = service; mSupervisor = supervisor; mInterceptor = new ActivityStartInterceptor(mService, mSupervisor); - mUsingVrCompatibilityDisplay = false; + mUsingVr2dDisplay = false; } final int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent, @@ -575,12 +575,15 @@ class ActivityStarter { return; } - if (startedActivityStackId == PINNED_STACK_ID - && (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP)) { + boolean clearedTask = (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) + == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); + if (startedActivityStackId == PINNED_STACK_ID && (result == START_TASK_TO_FRONT + || result == START_DELIVERED_TO_TOP || clearedTask)) { // The activity was already running in the pinned stack so it wasn't started, but either // brought to the front or the new intent was delivered to it since it was already in // front. Notify anyone interested in this piece of information. - mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt(); + mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt( + clearedTask); return; } } @@ -953,7 +956,7 @@ class ActivityStarter { // If we are not able to proceed, disassociate the activity from the task. Leaving an // activity in an incomplete state can lead to issues, such as performing operations // without a window container. - if (result != START_SUCCESS && mStartActivity.getTask() != null) { + if (result < START_SUCCESS && mStartActivity.getTask() != null) { mStartActivity.getTask().removeActivity(mStartActivity); } mService.mWindowManager.continueSurfaceLayout(); @@ -1059,7 +1062,7 @@ class ActivityStarter { // We didn't do anything... but it was needed (a.k.a., client don't use that // intent!) And for paranoia, make sure we have correctly resumed the top activity. resumeTargetStackIfNeeded(); - if (outActivity.length > 0) { + if (outActivity != null && outActivity.length > 0) { outActivity[0] = reusedActivity; } return START_TASK_TO_FRONT; @@ -1476,12 +1479,12 @@ class ActivityStarter { } // Get the virtual display id from ActivityManagerService. - int displayId = mService.mVrCompatibilityDisplayId; + int displayId = mService.mVr2dDisplayId; if (displayId != INVALID_DISPLAY) { if (DEBUG_STACK) { Slog.d(TAG, "getSourceDisplayId :" + displayId); } - mUsingVrCompatibilityDisplay = true; + mUsingVr2dDisplay = true; return displayId; } @@ -2105,8 +2108,8 @@ class ActivityStarter { return mSupervisor.getValidLaunchStackOnDisplay(launchDisplayId, r); } - // If we are using Vr compatibility display, find the virtual display stack. - if (mUsingVrCompatibilityDisplay) { + // If we are using Vr2d display, find the virtual display stack. + if (mUsingVr2dDisplay) { ActivityStack as = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r); if (DEBUG_STACK) { Slog.v(TAG, "Launch stack for app: " + r.toString() + diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index b025385413ea..fbc2bd270a75 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -103,7 +103,6 @@ final class ProcessRecord { int renderThreadTid; // TID for RenderThread boolean serviceb; // Process currently is on the service B list boolean serviceHighRam; // We are forcing to service B list due to its RAM use - boolean setIsForeground; // Running foreground UI when last set? boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle? boolean hasClientActivities; // Are there any client services with activities? boolean hasStartedServices; // Are there any started services running in this process? @@ -303,9 +302,8 @@ final class ProcessRecord { pw.print(" hasAboveClient="); pw.print(hasAboveClient); pw.print(" treatLikeActivity="); pw.println(treatLikeActivity); } - if (setIsForeground || foregroundServices || forcingToForeground != null) { - pw.print(prefix); pw.print("setIsForeground="); pw.print(setIsForeground); - pw.print(" foregroundServices="); pw.print(foregroundServices); + if (foregroundServices || forcingToForeground != null) { + pw.print(prefix); pw.print("foregroundServices="); pw.print(foregroundServices); pw.print(" forcingToForeground="); pw.println(forcingToForeground); } if (reportedInteraction || fgInteractionTime != 0) { diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java index 7d2bc5b51c2e..f5d7b6858b83 100644 --- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java @@ -104,7 +104,7 @@ class TaskChangeNotificationController { }; private final TaskStackConsumer mNotifyPinnedActivityRestartAttempt = (l, m) -> { - l.onPinnedActivityRestartAttempt(); + l.onPinnedActivityRestartAttempt(m.arg1 != 0); }; private final TaskStackConsumer mNotifyPinnedStackAnimationStarted = (l, m) -> { @@ -300,10 +300,11 @@ class TaskChangeNotificationController { * running in the pinned stack and the activity was not actually started, but the task is * either brought to the front or a new Intent is delivered to it. */ - void notifyPinnedActivityRestartAttempt() { + void notifyPinnedActivityRestartAttempt(boolean clearedTask) { mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG); final Message msg = - mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG); + mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG, + clearedTask ? 1 : 0, 0); forAllLocalListeners(mNotifyPinnedActivityRestartAttempt, msg); msg.sendToTarget(); } diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java index c0fb77f08647..c411bcec4540 100644 --- a/services/core/java/com/android/server/am/UidRecord.java +++ b/services/core/java/com/android/server/am/UidRecord.java @@ -35,6 +35,7 @@ public final class UidRecord { int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT; long lastBackgroundTime; boolean ephemeral; + boolean foregroundServices; boolean curWhitelist; boolean setWhitelist; boolean idle; @@ -102,6 +103,7 @@ public final class UidRecord { public void reset() { curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY; + foregroundServices = false; } public void updateHasInternetPermission() { @@ -131,6 +133,9 @@ public final class UidRecord { if (ephemeral) { sb.append(" ephemeral"); } + if (foregroundServices) { + sb.append(" fgServices"); + } if (curWhitelist) { sb.append(" whitelist"); } diff --git a/services/core/java/com/android/server/display/NightDisplayService.java b/services/core/java/com/android/server/display/NightDisplayService.java index d1275bb2224a..78498967b344 100644 --- a/services/core/java/com/android/server/display/NightDisplayService.java +++ b/services/core/java/com/android/server/display/NightDisplayService.java @@ -47,7 +47,6 @@ import com.android.server.SystemService; import com.android.server.twilight.TwilightListener; import com.android.server.twilight.TwilightManager; import com.android.server.twilight.TwilightState; -import com.android.server.vr.VrManagerService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.Calendar; @@ -62,7 +61,6 @@ public final class NightDisplayService extends SystemService implements NightDisplayController.Callback { private static final String TAG = "NightDisplayService"; - private static final boolean DEBUG = false; /** * The transition time, in milliseconds, for Night Display to turn on/off. @@ -151,8 +149,9 @@ public final class NightDisplayService extends SystemService @Override public void onBootPhase(int phase) { - if (phase == PHASE_SYSTEM_SERVICES_READY) { - IVrManager vrManager = (IVrManager) getBinderService(Context.VR_SERVICE); + if (phase >= PHASE_SYSTEM_SERVICES_READY) { + final IVrManager vrManager = IVrManager.Stub.asInterface( + getBinderService(Context.VR_SERVICE)); if (vrManager != null) { try { vrManager.registerListener(mVrStateCallbacks); @@ -160,7 +159,9 @@ public final class NightDisplayService extends SystemService Slog.e(TAG, "Failed to register VR mode state listener: " + e); } } - } else if (phase == PHASE_BOOT_COMPLETED) { + } + + if (phase >= PHASE_BOOT_COMPLETED) { mBootCompleted = true; // Register listeners now that boot is complete. @@ -284,12 +285,18 @@ public final class NightDisplayService extends SystemService if (mIsActivated == null || mIsActivated != activated) { Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display"); - if (mAutoMode != null) { - mAutoMode.onActivated(activated); + if (mIsActivated != null) { + Secure.putLongForUser(getContext().getContentResolver(), + Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, System.currentTimeMillis(), + mCurrentUser); } mIsActivated = activated; + if (mAutoMode != null) { + mAutoMode.onActivated(activated); + } + applyTint(false); } } @@ -401,7 +408,7 @@ public final class NightDisplayService extends SystemService * Set the color transformation {@code MATRIX_NIGHT} to the given color temperature. * * @param colorTemperature color temperature in Kelvin - * @param outTemp the 4x4 display transformation matrix for that color temperature + * @param outTemp the 4x4 display transformation matrix for that color temperature */ private void setMatrix(int colorTemperature, float[] outTemp) { if (outTemp.length != 16) { @@ -423,8 +430,22 @@ public final class NightDisplayService extends SystemService outTemp[10] = blue; } + private Calendar getLastActivatedTime() { + final ContentResolver cr = getContext().getContentResolver(); + final long lastActivatedTimeMillis = Secure.getLongForUser( + cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1, mCurrentUser); + if (lastActivatedTimeMillis < 0) { + return null; + } + + final Calendar lastActivatedTime = Calendar.getInstance(); + lastActivatedTime.setTimeInMillis(lastActivatedTimeMillis); + return lastActivatedTime; + } + private abstract class AutoMode implements NightDisplayController.Callback { public abstract void onStart(); + public abstract void onStop(); } @@ -438,7 +459,7 @@ public final class NightDisplayService extends SystemService private Calendar mLastActivatedTime; - public CustomAutoMode() { + CustomAutoMode() { mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); mTimeChangedReceiver = new BroadcastReceiver() { @Override @@ -452,10 +473,10 @@ public final class NightDisplayService extends SystemService final Calendar now = Calendar.getInstance(); final Calendar startTime = mStartTime.getDateTimeBefore(now); final Calendar endTime = mEndTime.getDateTimeAfter(startTime); - final boolean activated = now.before(endTime); - boolean setActivated = mIsActivated == null || mLastActivatedTime == null; - if (!setActivated && mIsActivated != activated) { + boolean activate = now.before(endTime); + if (mLastActivatedTime != null) { + // Convert mLastActivatedTime to the current timezone if needed. final TimeZone currentTimeZone = now.getTimeZone(); if (!currentTimeZone.equals(mLastActivatedTime.getTimeZone())) { final int year = mLastActivatedTime.get(Calendar.YEAR); @@ -470,17 +491,16 @@ public final class NightDisplayService extends SystemService mLastActivatedTime.set(Calendar.MINUTE, minute); } - if (mIsActivated) { - setActivated = now.before(mStartTime.getDateTimeBefore(mLastActivatedTime)) - || now.after(mEndTime.getDateTimeAfter(mLastActivatedTime)); - } else { - setActivated = now.before(mEndTime.getDateTimeBefore(mLastActivatedTime)) - || now.after(mStartTime.getDateTimeAfter(mLastActivatedTime)); + // Maintain the existing activated state if within the current period. + if (mLastActivatedTime.before(now) + && mLastActivatedTime.after(startTime) + && (mLastActivatedTime.after(endTime) || now.before(endTime))) { + activate = mController.isActivated(); } } - if (setActivated) { - mController.setActivated(activated); + if (mIsActivated == null || mIsActivated != activate) { + mController.setActivated(activate); } updateNextAlarm(mIsActivated, now); } @@ -502,6 +522,8 @@ public final class NightDisplayService extends SystemService mStartTime = mController.getCustomStartTime(); mEndTime = mController.getCustomEndTime(); + mLastActivatedTime = getLastActivatedTime(); + // Force an update to initialize state. updateActivated(); } @@ -516,11 +538,8 @@ public final class NightDisplayService extends SystemService @Override public void onActivated(boolean activated) { - final Calendar now = Calendar.getInstance(); - if (mIsActivated != null) { - mLastActivatedTime = now; - } - updateNextAlarm(activated, now); + mLastActivatedTime = getLastActivatedTime(); + updateNextAlarm(activated, Calendar.getInstance()); } @Override @@ -550,33 +569,33 @@ public final class NightDisplayService extends SystemService private Calendar mLastActivatedTime; - public TwilightAutoMode() { + TwilightAutoMode() { mTwilightManager = getLocalService(TwilightManager.class); } private void updateActivated(TwilightState state) { - final boolean isNight = state != null && state.isNight(); - boolean setActivated = mIsActivated == null || mIsActivated != isNight; - if (setActivated && state != null && mLastActivatedTime != null) { + boolean activate = state != null && state.isNight(); + if (state != null && mLastActivatedTime != null) { + final Calendar now = Calendar.getInstance(); final Calendar sunrise = state.sunrise(); final Calendar sunset = state.sunset(); - if (sunrise.before(sunset)) { - setActivated = mLastActivatedTime.before(sunrise) - || mLastActivatedTime.after(sunset); - } else { - setActivated = mLastActivatedTime.before(sunset) - || mLastActivatedTime.after(sunrise); + + // Maintain the existing activated state if within the current period. + if (mLastActivatedTime.before(now) + && (mLastActivatedTime.after(sunrise) ^ mLastActivatedTime.after(sunset))) { + activate = mController.isActivated(); } } - if (setActivated) { - mController.setActivated(isNight); + if (mIsActivated == null || mIsActivated != activate) { + mController.setActivated(activate); } } @Override public void onStart() { mTwilightManager.registerListener(this, mHandler); + mLastActivatedTime = getLastActivatedTime(); // Force an update to initialize state. updateActivated(mTwilightManager.getLastTwilightState()); @@ -591,7 +610,7 @@ public final class NightDisplayService extends SystemService @Override public void onActivated(boolean activated) { if (mIsActivated != null) { - mLastActivatedTime = Calendar.getInstance(); + mLastActivatedTime = getLastActivatedTime(); } } diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 91c32e48d802..51bebb0963c9 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -382,6 +382,8 @@ public class GnssLocationProvider implements LocationProviderInterface { // Wakelocks private final static String WAKELOCK_KEY = "GnssLocationProvider"; private final PowerManager.WakeLock mWakeLock; + private static final String DOWNLOAD_EXTRA_WAKELOCK_KEY = "GnssLocationProviderXtraDownload"; + private final PowerManager.WakeLock mDownloadXtraWakeLock; // Alarms private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP"; @@ -686,6 +688,11 @@ public class GnssLocationProvider implements LocationProviderInterface { mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY); mWakeLock.setReferenceCounted(true); + // Create a separate wake lock for xtra downloader as it may be released due to timeout. + mDownloadXtraWakeLock = mPowerManager.newWakeLock( + PowerManager.PARTIAL_WAKE_LOCK, DOWNLOAD_EXTRA_WAKELOCK_KEY); + mDownloadXtraWakeLock.setReferenceCounted(true); + mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0); mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0); @@ -989,7 +996,7 @@ public class GnssLocationProvider implements LocationProviderInterface { mDownloadXtraDataPending = STATE_DOWNLOADING; // hold wake lock while task runs - mWakeLock.acquire(DOWNLOAD_XTRA_DATA_TIMEOUT_MS); + mDownloadXtraWakeLock.acquire(DOWNLOAD_XTRA_DATA_TIMEOUT_MS); Log.i(TAG, "WakeLock acquired by handleDownloadXtraData()"); AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() { @Override @@ -1011,13 +1018,17 @@ public class GnssLocationProvider implements LocationProviderInterface { mXtraBackOff.nextBackoffMillis()); } - // release wake lock held by task - if (mWakeLock.isHeld()) { - mWakeLock.release(); - } else { - Log.e(TAG, "WakeLock expired before release in handleDownloadXtraData()"); + // Release wake lock held by task, synchronize on mLock in case multiple + // download tasks overrun. + synchronized (mLock) { + if (mDownloadXtraWakeLock.isHeld()) { + mDownloadXtraWakeLock.release(); + if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()"); + } else { + Log.e(TAG, "WakeLock expired before release in " + + "handleDownloadXtraData()"); + } } - Log.i(TAG, "WakeLock released by handleDownloadXtraData()"); } }); } diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java index f92bf3dd4427..4981d5c5731f 100644 --- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java +++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java @@ -66,7 +66,11 @@ public class NotificationIntrusivenessExtractor implements NotificationSignalExt @Override public void applyChangesLocked(NotificationRecord record) { - record.setRecentlyIntrusive(false); + // there will be another reconsideration in the message queue HANG_TIME_MS + // from each time this record alerts, which can finally clear this flag. + if ((System.currentTimeMillis() - record.getLastIntrusive()) >= HANG_TIME_MS) { + record.setRecentlyIntrusive(false); + } } }; } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index a8959c56ae22..1d843c1cabf8 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -884,6 +884,8 @@ public class NotificationManagerService extends SystemService { } else if (action.equals(Intent.ACTION_USER_REMOVED)) { final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); mZenModeHelper.onUserRemoved(user); + mRankingHelper.onUserRemoved(user); + savePolicyFile(); } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) { final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); mConditionProviders.onUserUnlocked(user); @@ -2869,7 +2871,8 @@ public class NotificationManagerService extends SystemService { adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY, System.currentTimeMillis()); summaryRecord = new NotificationRecord(getContext(), summarySbn, - notificationRecord.getChannel()); + notificationRecord.getChannel(), mRankingHelper.supportsChannels( + summarySbn.getPackageName(), summarySbn.getUid())); summaries.put(pkg, summarySbn.getKey()); } } @@ -3198,10 +3201,8 @@ public class NotificationManagerService extends SystemService { + ", incomingUserId=" + incomingUserId + ", notificationUid=" + notificationUid + ", notification=" + notification; - // STOPSHIP TODO: should throw instead of logging or toasting. - // throw new IllegalArgumentException(noChannelStr); Log.e(TAG, noChannelStr); - doDebugOnlyToast("Developer warning for package \"" + pkg + "\"\n" + + doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" + "Failed to post notification on channel \"" + channelId + "\"\n" + "See log for more details"); return; @@ -3210,7 +3211,8 @@ public class NotificationManagerService extends SystemService { final StatusBarNotification n = new StatusBarNotification( pkg, opPkg, id, tag, notificationUid, callingPid, notification, user, null, System.currentTimeMillis()); - final NotificationRecord r = new NotificationRecord(getContext(), n, channel); + final NotificationRecord r = new NotificationRecord(getContext(), n, channel, + mRankingHelper.supportsChannels(pkg, notificationUid)); if (!checkDisqualifyingFeatures(userId, notificationUid, id,tag, r)) { return; @@ -3236,8 +3238,10 @@ public class NotificationManagerService extends SystemService { mHandler.post(new EnqueueNotificationRunnable(userId, r)); } - private void doDebugOnlyToast(CharSequence toastText) { - if (Build.IS_DEBUGGABLE) { + private void doChannelWarningToast(CharSequence toastText) { + final boolean warningEnabled = Settings.System.getInt(getContext().getContentResolver(), + Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, 0) != 0; + if (warningEnabled || Build.IS_DEBUGGABLE) { try { Toast toast = Toast.makeText(getContext(), toastText, Toast.LENGTH_LONG); toast.show(); diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index b51a4d10a053..f019a5ce00ea 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -85,6 +85,7 @@ public final class NotificationRecord { // to communicate with the ranking module. private float mContactAffinity; private boolean mRecentlyIntrusive; + private long mLastIntrusive; // is this notification currently being intercepted by Zen Mode? private boolean mIntercept; @@ -115,7 +116,7 @@ public final class NotificationRecord { private int mSuppressedVisualEffects = 0; private String mUserExplanation; private String mPeopleExplanation; - private boolean mPreChannelsNotification = true; + private boolean mSupportsChannels = false; private Uri mSound; private long[] mVibration; private AudioAttributes mAttributes; @@ -128,7 +129,7 @@ public final class NotificationRecord { @VisibleForTesting public NotificationRecord(Context context, StatusBarNotification sbn, - NotificationChannel channel) + NotificationChannel channel, boolean supportsChannels) { this.sbn = sbn; mOriginalFlags = sbn.getNotification().flags; @@ -138,7 +139,7 @@ public final class NotificationRecord { mContext = context; stats = new NotificationUsageStats.SingleNotificationStats(); mChannel = channel; - mPreChannelsNotification = isPreChannelsNotification(); + mSupportsChannels = supportsChannels; mSound = calculateSound(); mVibration = calculateVibration(); mAttributes = calculateAttributes(); @@ -146,27 +147,11 @@ public final class NotificationRecord { mLight = calculateLights(); } - private boolean isPreChannelsNotification() { - try { - if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(getChannel().getId())) { - final ApplicationInfo applicationInfo = - mContext.getPackageManager().getApplicationInfoAsUser(sbn.getPackageName(), - 0, UserHandle.getUserId(sbn.getUid())); - if (applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) { - return true; - } - } - } catch (NameNotFoundException e) { - Slog.e(TAG, "Can't find package", e); - } - return false; - } - private Uri calculateSound() { final Notification n = sbn.getNotification(); Uri sound = mChannel.getSound(); - if (mPreChannelsNotification && (getChannel().getUserLockedFields() + if (!mSupportsChannels && (getChannel().getUserLockedFields() & NotificationChannel.USER_LOCKED_SOUND) == 0) { final boolean useDefaultSound = (n.defaults & Notification.DEFAULT_SOUND) != 0; @@ -191,7 +176,7 @@ public final class NotificationRecord { : defaultLightColor; Light light = getChannel().shouldShowLights() ? new Light(channelLightColor, defaultLightOn, defaultLightOff) : null; - if (mPreChannelsNotification + if (!mSupportsChannels && (getChannel().getUserLockedFields() & NotificationChannel.USER_LOCKED_LIGHTS) == 0) { final Notification notification = sbn.getNotification(); @@ -222,7 +207,7 @@ public final class NotificationRecord { } else { vibration = null; } - if (mPreChannelsNotification + if (!mSupportsChannels && (getChannel().getUserLockedFields() & NotificationChannel.USER_LOCKED_VIBRATION) == 0) { final Notification notification = sbn.getNotification(); @@ -244,7 +229,7 @@ public final class NotificationRecord { attributes = Notification.AUDIO_ATTRIBUTES_DEFAULT; } - if (mPreChannelsNotification + if (!mSupportsChannels && (getChannel().getUserLockedFields() & NotificationChannel.USER_LOCKED_SOUND) == 0) { if (n.audioAttributes != null) { @@ -293,7 +278,7 @@ public final class NotificationRecord { stats.requestedImportance = requestedImportance; stats.isNoisy = mSound != null || mVibration != null; - if (mPreChannelsNotification + if (!mSupportsChannels && (importance == IMPORTANCE_UNSPECIFIED || (getChannel().getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0)) { @@ -460,7 +445,7 @@ public final class NotificationRecord { pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs); pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs); pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects); - if (mPreChannelsNotification) { + if (!mSupportsChannels) { pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x", notification.defaults, notification.flags)); pw.println(prefix + "n.sound=" + notification.sound); @@ -531,12 +516,19 @@ public final class NotificationRecord { public void setRecentlyIntrusive(boolean recentlyIntrusive) { mRecentlyIntrusive = recentlyIntrusive; + if (recentlyIntrusive) { + mLastIntrusive = System.currentTimeMillis(); + } } public boolean isRecentlyIntrusive() { return mRecentlyIntrusive; } + public long getLastIntrusive() { + return mLastIntrusive; + } + public void setPackagePriority(int packagePriority) { mPackagePriority = packagePriority; } diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java index 4d19b52c327a..14d796f59c37 100644 --- a/services/core/java/com/android/server/notification/RankingConfig.java +++ b/services/core/java/com/android/server/notification/RankingConfig.java @@ -42,4 +42,6 @@ public interface RankingConfig { void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId); void permanentlyDeleteNotificationChannels(String pkg, int uid); ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid, boolean includeDeleted); + + boolean supportsChannels(String pkg, int uid); } diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index 850b730f3dff..1e741de5f269 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -273,8 +273,14 @@ public class RankingHelper implements RankingConfig { } private boolean shouldHaveDefaultChannel(Record r) throws NameNotFoundException { + if (supportsChannels(r)) { + return false; + } + final int userId = UserHandle.getUserId(r.uid); - final ApplicationInfo applicationInfo = mPm.getApplicationInfoAsUser(r.pkg, 0, userId); + final ApplicationInfo applicationInfo = mPm.getApplicationInfoAsUser(r.pkg, + PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, + userId); if (applicationInfo.targetSdkVersion > Build.VERSION_CODES.N_MR1) { // O apps should not have the default channel. return false; @@ -500,6 +506,31 @@ public class RankingHelper implements RankingConfig { } @Override + public boolean supportsChannels(String pkg, int uid) { + Record r = getOrCreateRecord(pkg, uid); + + if (r == null) { + return false; + } + + if (r.channels.size() == 1 + && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) { + return false; + } + + return true; + } + + private boolean supportsChannels(Record r) { + if (r.channels.size() == 1 + && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) { + return false; + } + + return (r.channels.size() > 0); + } + + @Override public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group, boolean fromTargetApp) { Preconditions.checkNotNull(pkg); @@ -571,6 +602,10 @@ public class RankingHelper implements RankingConfig { r.channels.put(channel.getId(), channel); MetricsLogger.action(getChannelLog(channel, pkg).setType( MetricsProto.MetricsEvent.TYPE_OPEN)); + + // Remove Default Channel. + r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID); + updateConfig(); } @@ -667,13 +702,7 @@ public class RankingHelper implements RankingConfig { if (r == null) { return; } - int N = r.channels.size() - 1; - for (int i = N; i >= 0; i--) { - String key = r.channels.keyAt(i); - if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) { - r.channels.remove(key); - } - } + r.channels.clear(); updateConfig(); } @@ -1011,6 +1040,18 @@ public class RankingHelper implements RankingConfig { return packageChannels; } + public void onUserRemoved(int userId) { + synchronized (mRecords) { + int N = mRecords.size(); + for (int i = N - 1; i >= 0 ; i--) { + Record record = mRecords.valueAt(i); + if (UserHandle.getUserId(record.uid) == userId) { + mRecords.removeAt(i); + } + } + } + } + public void onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList, int[] uidList) { if (pkgList == null || pkgList.length == 0) { @@ -1025,6 +1066,8 @@ public class RankingHelper implements RankingConfig { final int uid = uidList[i]; synchronized (mRecords) { mRecords.remove(recordKey(pkg, uid)); + // reset to default settings and re-add misc channel for pre-O apps + getOrCreateRecord(pkg, uid); } mRestoredWithoutUids.remove(pkg); updated = true; diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java index 9d08004c579a..09e9433c9a02 100644 --- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java +++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java @@ -43,6 +43,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.NoSuchElementException; import java.util.concurrent.TimeoutException; /** @@ -206,7 +207,9 @@ final class EphemeralResolverConnection implements DeathRecipient { private void handleBinderDiedLocked() { if (mRemoteInstance != null) { - mRemoteInstance.asBinder().unlinkToDeath(this, 0 /*flags*/); + try { + mRemoteInstance.asBinder().unlinkToDeath(this, 0 /*flags*/); + } catch (NoSuchElementException ignore) { } } mRemoteInstance = null; } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 4c0d9da52f37..f94a56f15fe0 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -24,6 +24,7 @@ import android.content.pm.PackageParser; import android.os.Environment; import android.os.FileUtils; import android.os.PowerManager; +import android.os.SystemClock; import android.os.UserHandle; import android.os.WorkSource; import android.util.Log; @@ -54,6 +55,7 @@ import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; +import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT; import static com.android.server.pm.PackageManagerServiceCompilerMapping.getNonProfileGuidedCompilerFilter; import static dalvik.system.DexFile.isProfileGuidedCompilerFilter; @@ -67,13 +69,17 @@ public class PackageDexOptimizer { public static final int DEX_OPT_SKIPPED = 0; public static final int DEX_OPT_PERFORMED = 1; public static final int DEX_OPT_FAILED = -1; + // One minute over PM WATCHDOG_TIMEOUT + private static final long WAKELOCK_TIMEOUT_MS = WATCHDOG_TIMEOUT + 1000 * 60; /** Special library name that skips shared libraries check during compilation. */ public static final String SKIP_SHARED_LIBRARY_CHECK = "&"; + @GuardedBy("mInstallLock") private final Installer mInstaller; private final Object mInstallLock; + @GuardedBy("mInstallLock") private final PowerManager.WakeLock mDexoptWakeLock; private volatile boolean mSystemReady; @@ -111,21 +117,12 @@ public class PackageDexOptimizer { return DEX_OPT_SKIPPED; } synchronized (mInstallLock) { - // During boot the system doesn't need to instantiate and obtain a wake lock. - // PowerManager might not be ready, but that doesn't mean that we can't proceed with - // dexopt. - final boolean useLock = mSystemReady; - if (useLock) { - mDexoptWakeLock.setWorkSource(new WorkSource(pkg.applicationInfo.uid)); - mDexoptWakeLock.acquire(); - } + final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid); try { return performDexOptLI(pkg, sharedLibraries, instructionSets, checkProfiles, targetCompilationFilter, packageStats, isUsedByOtherApps); } finally { - if (useLock) { - mDexoptWakeLock.release(); - } + releaseWakeLockLI(acquireTime); } } } @@ -250,22 +247,46 @@ public class PackageDexOptimizer { public int dexOptSecondaryDexPath(ApplicationInfo info, String path, Set<String> isas, String compilerFilter, boolean isUsedByOtherApps) { synchronized (mInstallLock) { - // During boot the system doesn't need to instantiate and obtain a wake lock. - // PowerManager might not be ready, but that doesn't mean that we can't proceed with - // dexopt. - final boolean useLock = mSystemReady; - if (useLock) { - mDexoptWakeLock.setWorkSource(new WorkSource(info.uid)); - mDexoptWakeLock.acquire(); - } + final long acquireTime = acquireWakeLockLI(info.uid); try { return dexOptSecondaryDexPathLI(info, path, isas, compilerFilter, isUsedByOtherApps); } finally { - if (useLock) { - mDexoptWakeLock.release(); - } + releaseWakeLockLI(acquireTime); + } + } + } + + @GuardedBy("mInstallLock") + private long acquireWakeLockLI(final int uid) { + // During boot the system doesn't need to instantiate and obtain a wake lock. + // PowerManager might not be ready, but that doesn't mean that we can't proceed with + // dexopt. + if (!mSystemReady) { + return -1; + } + mDexoptWakeLock.setWorkSource(new WorkSource(uid)); + mDexoptWakeLock.acquire(WAKELOCK_TIMEOUT_MS); + return SystemClock.elapsedRealtime(); + } + + @GuardedBy("mInstallLock") + private void releaseWakeLockLI(final long acquireTime) { + if (acquireTime < 0) { + return; + } + try { + if (mDexoptWakeLock.isHeld()) { + mDexoptWakeLock.release(); + } + final long duration = SystemClock.elapsedRealtime() - acquireTime; + if (duration >= WAKELOCK_TIMEOUT_MS) { + Slog.wtf(TAG, "WakeLock " + mDexoptWakeLock.getTag() + + " time out. Operation took " + duration + " ms. Thread: " + + Thread.currentThread().getName()); } + } catch (Exception e) { + Slog.wtf(TAG, "Error while releasing " + mDexoptWakeLock.getTag() + " lock", e); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 2686fcb866c1..cfcd0a51a328 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -444,7 +444,7 @@ public class PackageManagerService extends IPackageManager.Stub * minute but we sometimes do very lengthy I/O operations on this thread, * such as installing multi-gigabyte applications, so ours needs to be longer. */ - private static final long WATCHDOG_TIMEOUT = 1000*60*10; // ten minutes + static final long WATCHDOG_TIMEOUT = 1000*60*10; // ten minutes /** * Wall-clock timeout (in milliseconds) after which we *require* that an fstrim diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index 3d7cedce522a..9b44646efc0a 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -60,6 +60,7 @@ public class DexManager { // Maps package name to code locations. // It caches the code locations for the installed packages. This allows for // faster lookups (no locks) when finding what package owns the dex file. + @GuardedBy("mPackageCodeLocationsCache") private final Map<String, PackageCodeLocations> mPackageCodeLocationsCache; // PackageDexUsage handles the actual I/O operations. It is responsible to @@ -204,7 +205,7 @@ public class DexManager { // In case there was an update, write the package use info to disk async. // Note that we do the writing here and not in PackageDexUsage in order to be // consistent with other methods in DexManager (e.g. reconcileSecondaryDexFiles performs - // multiple updates in PackaeDexUsage before writing it). + // multiple updates in PackageDexUsage before writing it). if (mPackageDexUsage.clearUsedByOtherApps(packageName)) { mPackageDexUsage.maybeWriteAsync(); } @@ -224,7 +225,7 @@ public class DexManager { // In case there was an update, write the package use info to disk async. // Note that we do the writing here and not in PackageDexUsage in order to be // consistent with other methods in DexManager (e.g. reconcileSecondaryDexFiles performs - // multiple updates in PackaeDexUsage before writing it). + // multiple updates in PackageDexUsage before writing it). if (updated) { mPackageDexUsage.maybeWriteAsync(); } @@ -243,17 +244,22 @@ public class DexManager { private void cachePackageCodeLocation(String packageName, String baseCodePath, String[] splitCodePaths, String[] dataDirs, int userId) { - PackageCodeLocations pcl = putIfAbsent(mPackageCodeLocationsCache, packageName, - new PackageCodeLocations(packageName, baseCodePath, splitCodePaths)); - pcl.updateCodeLocation(baseCodePath, splitCodePaths); - if (dataDirs != null) { - for (String dataDir : dataDirs) { - // The set of data dirs includes deviceProtectedDataDir and - // credentialProtectedDataDir which might be null for shared - // libraries. Currently we don't track these but be lenient - // and check in case we ever decide to store their usage data. - if (dataDir != null) { - pcl.mergeAppDataDirs(dataDir, userId); + synchronized (mPackageCodeLocationsCache) { + PackageCodeLocations pcl = putIfAbsent(mPackageCodeLocationsCache, packageName, + new PackageCodeLocations(packageName, baseCodePath, splitCodePaths)); + // TODO(calin): We are forced to extend the scope of this synchronization because + // the values of the cache (PackageCodeLocations) are updated in place. + // Make PackageCodeLocations immutable to simplify the synchronization reasoning. + pcl.updateCodeLocation(baseCodePath, splitCodePaths); + if (dataDirs != null) { + for (String dataDir : dataDirs) { + // The set of data dirs includes deviceProtectedDataDir and + // credentialProtectedDataDir which might be null for shared + // libraries. Currently we don't track these but be lenient + // and check in case we ever decide to store their usage data. + if (dataDir != null) { + pcl.mergeAppDataDirs(dataDir, userId); + } } } } @@ -479,10 +485,12 @@ public class DexManager { // The loadingPackage does not own the dex file. // Perform a reverse look-up in the cache to detect if any package has ownership. // Note that we can have false negatives if the cache falls out of date. - for (PackageCodeLocations pcl : mPackageCodeLocationsCache.values()) { - outcome = pcl.searchDex(dexPath, userId); - if (outcome != DEX_SEARCH_NOT_FOUND) { - return new DexSearchResult(pcl.mPackageName, outcome); + synchronized (mPackageCodeLocationsCache) { + for (PackageCodeLocations pcl : mPackageCodeLocationsCache.values()) { + outcome = pcl.searchDex(dexPath, userId); + if (outcome != DEX_SEARCH_NOT_FOUND) { + return new DexSearchResult(pcl.mPackageName, outcome); + } } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 7e03d80d33a6..f0ae1a735443 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -184,6 +184,7 @@ import android.provider.Settings; import android.service.dreams.DreamManagerInternal; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; +import android.service.vr.IPersistentVrStateCallbacks; import android.speech.RecognizerIntent; import android.telecom.TelecomManager; import android.util.DisplayMetrics; @@ -512,6 +513,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { volatile boolean mGoingToSleep; volatile boolean mRecentsVisible; volatile boolean mPictureInPictureVisible; + // Written by vr manager thread, only read in this class. + volatile private boolean mPersistentVrModeEnabled; volatile private boolean mDismissImeOnBackKeyPressed; // Used to hold the last user key used to wake the device. This helps us prevent up events @@ -581,6 +584,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mTranslucentDecorEnabled = true; boolean mUseTvRouting; + private boolean mHandleVolumeKeysInWM; + int mPointerLocationMode = 0; // guarded by mLock // The last window we were told about in focusChanged. @@ -777,6 +782,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean mBugreportTvKey2Pressed; private boolean mBugreportTvScheduled; + private boolean mAccessibilityTvKey1Pressed; + private boolean mAccessibilityTvKey2Pressed; + private boolean mAccessibilityTvScheduled; + /* The number of steps between min and max brightness */ private static final int BRIGHTNESS_STEPS = 10; @@ -821,6 +830,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_BACK_DELAYED_PRESS = 20; private static final int MSG_ACCESSIBILITY_SHORTCUT = 21; private static final int MSG_BUGREPORT_TV = 22; + private static final int MSG_ACCESSIBILITY_TV = 23; private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0; private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1; @@ -902,6 +912,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_BUGREPORT_TV: takeBugreport(); break; + case MSG_ACCESSIBILITY_TV: + if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(false)) { + accessibilityShortcutActivated(); + } + break; } } } @@ -1002,6 +1017,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { } MyOrientationListener mOrientationListener; + final IPersistentVrStateCallbacks mPersistentVrModeListener = + new IPersistentVrStateCallbacks.Stub() { + @Override + public void onPersistentVrStateChanged(boolean enabled) { + mPersistentVrModeEnabled = enabled; + } + }; + private final StatusBarController mStatusBarController = new StatusBarController(); private final BarController mNavigationBarController = new BarController("NavigationBar", @@ -1917,6 +1940,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION; + mHandleVolumeKeysInWM = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_handleVolumeKeysInWindowManager); + readConfigurationDependentBehaviors(); mAccessibilityManager = (AccessibilityManager) context.getSystemService( @@ -3308,6 +3334,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (!down) { cancelPreloadRecentApps(); + if (mHasFeatureLeanback) { + // Clear flags + mAccessibilityTvKey2Pressed = down; + } + mHomePressed = false; if (mHomeConsumed) { mHomeConsumed = false; @@ -3362,6 +3393,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { preloadRecentApps(); } } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { + if (mHasFeatureLeanback) { + mAccessibilityTvKey2Pressed = down; + if (interceptAccessibilityGestureTv()) { + return -1; + } + } + if (!keyguardOn) { handleLongPressOnHome(event.getDeviceId()); } @@ -3511,16 +3549,28 @@ public class PhoneWindowManager implements WindowManagerPolicy { } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) { - if (mUseTvRouting) { - // On TVs volume keys never go to the foreground app. + if (mUseTvRouting || mHandleVolumeKeysInWM) { + // On TVs or when the configuration is enabled, volume keys never + // go to the foreground app. dispatchDirectAudioEvent(event); return -1; } + + // If the device is in Vr mode, drop the volume keys and don't + // forward it to the application/dispatch the audio event. + if (mPersistentVrModeEnabled) { + return -1; + } } else if (keyCode == KeyEvent.KEYCODE_TAB && event.isMetaPressed()) { // Pass through keyboard navigation keys. return 0; } else if (mHasFeatureLeanback && interceptBugreportGestureTv(keyCode, down)) { return -1; + } else if (mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { + mAccessibilityTvKey1Pressed = down; + if (interceptAccessibilityGestureTv()) { + return -1; + } } // Toggle Caps Lock on META-ALT. @@ -3739,6 +3789,25 @@ public class PhoneWindowManager implements WindowManagerPolicy { return mBugreportTvScheduled; } + /** + * TV only: recognizes a remote control gesture as Accessibility shortcut. + * Shortcut: Long press (HOME + DPAD_CENTER) + */ + private boolean interceptAccessibilityGestureTv() { + if (mAccessibilityTvKey1Pressed && mAccessibilityTvKey2Pressed) { + if (!mAccessibilityTvScheduled) { + mAccessibilityTvScheduled = true; + Message msg = Message.obtain(mHandler, MSG_ACCESSIBILITY_TV); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); + } + } else if (mAccessibilityTvScheduled) { + mHandler.removeMessages(MSG_ACCESSIBILITY_TV); + mAccessibilityTvScheduled = false; + } + return mAccessibilityTvScheduled; + } + private void takeBugreport() { if ("1".equals(SystemProperties.get("ro.debuggable")) || Settings.Global.getInt(mContext.getContentResolver(), @@ -5900,8 +5969,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } } - if (mUseTvRouting) { - // On TVs, defer special key handlings to + if (mUseTvRouting || mHandleVolumeKeysInWM) { + // Defer special key handlings to // {@link interceptKeyBeforeDispatching()}. result |= ACTION_PASS_TO_USER; } else if ((result & ACTION_PASS_TO_USER) == 0) { @@ -6909,7 +6978,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { || mAllowAllRotations == 1 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) { - preferredRotation = sensorRotation; + // In VrMode, we report the sensor as always being in default orientation so: + // 1) The orientation doesn't change as the user moves their head. + // 2) 2D apps within VR show in the device's default orientation. + // This only overwrites the sensor-provided orientation and does not affect any + // explicit orientation preferences specified by any activities. + preferredRotation = + mPersistentVrModeEnabled ? Surface.ROTATION_0 : sensorRotation; } else { preferredRotation = lastRotation; } @@ -7083,6 +7158,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { mKeyguardDelegate.onSystemReady(); mVrManagerInternal = LocalServices.getService(VrManagerInternal.class); + if (mVrManagerInternal != null) { + mVrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener); + } readCameraLensCoverState(); updateUiMode(); @@ -8215,6 +8293,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(prefix); pw.print("mDemoHdmiRotation="); pw.print(mDemoHdmiRotation); pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock); pw.print(prefix); pw.print("mUndockedHdmiRotation="); pw.println(mUndockedHdmiRotation); + if (mHasFeatureLeanback) { + pw.print(prefix); + pw.print("mAccessibilityTvKey1Pressed="); pw.println(mAccessibilityTvKey1Pressed); + pw.print(prefix); + pw.print("mAccessibilityTvKey2Pressed="); pw.println(mAccessibilityTvKey2Pressed); + pw.print(prefix); + pw.print("mAccessibilityTvScheduled="); pw.println(mAccessibilityTvScheduled); + } mGlobalKeyManager.dump(prefix, pw); mStatusBarController.dump(pw, prefix); diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index f4f7e2403f0c..8e06a51238ee 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -325,8 +325,6 @@ public class TrustManagerService extends SystemService { agentInfo.label = resolveInfo.loadLabel(pm); agentInfo.icon = resolveInfo.loadIcon(pm); agentInfo.settings = getSettingsAttrs(pm, resolveInfo); - agentInfo.agent = new TrustAgentWrapper(mContext, this, - new Intent().setComponent(name), userInfo.getUserHandle()); } else { int index = mActiveAgents.indexOf(agentInfo); agentInfo = mActiveAgents.valueAt(index); @@ -363,6 +361,11 @@ public class TrustManagerService extends SystemService { } } + if (agentInfo.agent == null) { + agentInfo.agent = new TrustAgentWrapper(mContext, this, + new Intent().setComponent(name), userInfo.getUserHandle()); + } + if (!mActiveAgents.contains(agentInfo)) { mActiveAgents.add(agentInfo); } else { diff --git a/services/core/java/com/android/server/twilight/TwilightState.java b/services/core/java/com/android/server/twilight/TwilightState.java index a12965df11c0..30a8cccb6ad5 100644 --- a/services/core/java/com/android/server/twilight/TwilightState.java +++ b/services/core/java/com/android/server/twilight/TwilightState.java @@ -31,7 +31,7 @@ public final class TwilightState { private final long mSunriseTimeMillis; private final long mSunsetTimeMillis; - TwilightState(long sunriseTimeMillis, long sunsetTimeMillis) { + public TwilightState(long sunriseTimeMillis, long sunsetTimeMillis) { mSunriseTimeMillis = sunriseTimeMillis; mSunsetTimeMillis = sunsetTimeMillis; } diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java index 63c61955bcd7..1f7564027166 100644 --- a/services/core/java/com/android/server/vr/VrManagerInternal.java +++ b/services/core/java/com/android/server/vr/VrManagerInternal.java @@ -16,7 +16,7 @@ package com.android.server.vr; import android.annotation.NonNull; -import android.app.CompatibilityDisplayProperties; +import android.app.Vr2dDisplayProperties; import android.content.ComponentName; import android.service.vr.IPersistentVrStateCallbacks; @@ -83,16 +83,16 @@ public abstract class VrManagerInternal { public abstract int hasVrPackage(@NonNull ComponentName packageName, int userId); /** - * Sets the resolution and DPI of the compatibility virtual display used to display + * Sets the resolution and DPI of the vr2d virtual display used to display * 2D applications in VR mode. * * <p>Requires {@link android.Manifest.permission#ACCESS_VR_MANAGER} permission.</p> * - * @param compatDisplayProp Properties of the virtual display for 2D applications + * @param vr2dDisplayProp Properties of the virtual display for 2D applications * in VR mode. */ - public abstract void setCompatibilityDisplayProperties( - CompatibilityDisplayProperties compatDisplayProp); + public abstract void setVr2dDisplayProperties( + Vr2dDisplayProperties vr2dDisplayProp); /** * Sets the persistent VR mode state of a device. When a device is in persistent VR mode it will @@ -110,7 +110,7 @@ public abstract class VrManagerInternal { * @return {@link android.view.Display.INVALID_DISPLAY} if there is no virtual display * currently, else return the display id of the virtual display */ - public abstract int getCompatibilityDisplayId(); + public abstract int getVr2dDisplayId(); /** * Adds listener that reports state changes to persistent VR mode. diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java index 860b2419327a..0e183f0a50b8 100644 --- a/services/core/java/com/android/server/vr/VrManagerService.java +++ b/services/core/java/com/android/server/vr/VrManagerService.java @@ -21,7 +21,7 @@ import android.Manifest; import android.app.ActivityManagerInternal; import android.app.ActivityManager; import android.app.AppOpsManager; -import android.app.CompatibilityDisplayProperties; +import android.app.Vr2dDisplayProperties; import android.app.NotificationManager; import android.annotation.NonNull; import android.content.ComponentName; @@ -141,7 +141,7 @@ public class VrManagerService extends SystemService implements EnabledComponentC private final NotificationAccessManager mNotifAccessManager = new NotificationAccessManager(); /** Tracks the state of the screen and keyguard UI.*/ private int mSystemSleepFlags = FLAG_AWAKE; - private CompatibilityDisplay mCompatibilityDisplay; + private Vr2dDisplay mVr2dDisplay; private static final int MSG_VR_STATE_CHANGE = 0; private static final int MSG_PENDING_VR_STATE_CHANGE = 1; @@ -428,15 +428,15 @@ public class VrManagerService extends SystemService implements EnabledComponentC } @Override - public void setCompatibilityDisplayProperties( - CompatibilityDisplayProperties compatDisplayProp) { + public void setVr2dDisplayProperties( + Vr2dDisplayProperties vr2dDisplayProp) { enforceCallerPermission(Manifest.permission.RESTRICTED_VR_ACCESS); - VrManagerService.this.setCompatibilityDisplayProperties(compatDisplayProp); + VrManagerService.this.setVr2dDisplayProperties(vr2dDisplayProp); } @Override - public int getCompatibilityDisplayId() { - return VrManagerService.this.getCompatibilityDisplayId(); + public int getVr2dDisplayId() { + return VrManagerService.this.getVr2dDisplayId(); } @Override @@ -549,14 +549,14 @@ public class VrManagerService extends SystemService implements EnabledComponentC } @Override - public void setCompatibilityDisplayProperties( - CompatibilityDisplayProperties compatDisplayProp) { - VrManagerService.this.setCompatibilityDisplayProperties(compatDisplayProp); + public void setVr2dDisplayProperties( + Vr2dDisplayProperties compatDisplayProp) { + VrManagerService.this.setVr2dDisplayProperties(compatDisplayProp); } @Override - public int getCompatibilityDisplayId() { - return VrManagerService.this.getCompatibilityDisplayId(); + public int getVr2dDisplayId() { + return VrManagerService.this.getVr2dDisplayId(); } @Override @@ -608,8 +608,8 @@ public class VrManagerService extends SystemService implements EnabledComponentC DisplayManager dm = (DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE); ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class); - mCompatibilityDisplay = new CompatibilityDisplay(dm, ami, mVrManager); - mCompatibilityDisplay.init(getContext()); + mVr2dDisplay = new Vr2dDisplay(dm, ami, mVrManager); + mVr2dDisplay.init(getContext()); } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { synchronized (mLock) { mVrModeAllowed = true; @@ -1116,20 +1116,20 @@ public class VrManagerService extends SystemService implements EnabledComponentC } } - public void setCompatibilityDisplayProperties( - CompatibilityDisplayProperties compatDisplayProp) { - if (mCompatibilityDisplay != null) { - mCompatibilityDisplay.setVirtualDisplayProperties(compatDisplayProp); + public void setVr2dDisplayProperties( + Vr2dDisplayProperties compatDisplayProp) { + if (mVr2dDisplay != null) { + mVr2dDisplay.setVirtualDisplayProperties(compatDisplayProp); return; } - Slog.w(TAG, "CompatibilityDisplay is null!"); + Slog.w(TAG, "Vr2dDisplay is null!"); } - private int getCompatibilityDisplayId() { - if (mCompatibilityDisplay != null) { - return mCompatibilityDisplay.getVirtualDisplayId(); + private int getVr2dDisplayId() { + if (mVr2dDisplay != null) { + return mVr2dDisplay.getVirtualDisplayId(); } - Slog.w(TAG, "CompatibilityDisplay is null!"); + Slog.w(TAG, "Vr2dDisplay is null!"); return INVALID_DISPLAY; } diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java index c625cbea1fa4..b5e194b4b93b 100644 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java @@ -36,7 +36,6 @@ import android.graphics.Rect; import android.os.Debug; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.Trace; import android.util.Slog; import android.view.IApplicationToken; @@ -319,7 +318,7 @@ public class AppWindowContainerController + " token: " + mToken); return; } - mContainer.setDisablePreviewSnapshots(disable); + mContainer.setDisablePreviewScreenshots(disable); } } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 36418bea1940..640bac28efdf 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -23,6 +23,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; +import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; @@ -48,6 +49,7 @@ import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE import static com.android.server.wm.WindowManagerService.logWithStack; import android.annotation.NonNull; +import android.app.Activity; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; @@ -1528,12 +1530,24 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return candidate; } - void setDisablePreviewSnapshots(boolean disable) { + /** + * See {@link Activity#setDisablePreviewScreenshots}. + */ + void setDisablePreviewScreenshots(boolean disable) { mDisbalePreviewScreenshots = disable; } - boolean shouldDisablePreviewScreenshots() { - return mDisbalePreviewScreenshots; + /** + * Retrieves whether we'd like to generate a snapshot that's based solely on the theme. This is + * the case when preview screenshots are disabled {@link #setDisablePreviewScreenshots} or when + * we can't take a snapshot for other reasons, for example, if we have a secure window. + * + * @return True if we need to generate an app theme snapshot, false if we'd like to take a real + * screenshot. + */ + boolean shouldUseAppThemeSnapshot() { + return mDisbalePreviewScreenshots || forAllWindows(w -> (w.mAttrs.flags & FLAG_SECURE) != 0, + true /* topToBottom */); } @Override diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java index 410044633215..d44cd13f8ed5 100644 --- a/services/core/java/com/android/server/wm/DimLayerController.java +++ b/services/core/java/com/android/server/wm/DimLayerController.java @@ -188,6 +188,7 @@ class DimLayerController { boolean animateDimLayers() { int fullScreen = -1; int fullScreenAndDimming = -1; + int topFullScreenUserLayer = 0; boolean result = false; for (int i = mState.size() - 1; i >= 0; i--) { @@ -213,8 +214,18 @@ class DimLayerController { // and we have to make sure we always animate the layer. if (user.dimFullscreen() && state.dimLayer == mSharedFullScreenDimLayer) { fullScreen = i; - if (mState.valueAt(i).continueDimming) { + if (!state.continueDimming) { + continue; + } + + // When choosing which user to assign the shared fullscreen layer to + // we need to look at Z-order. + if (topFullScreenUserLayer == 0 || + (state.animator != null && state.animator.mAnimLayer > topFullScreenUserLayer)) { fullScreenAndDimming = i; + if (state.animator != null) { + topFullScreenUserLayer = state.animator.mAnimLayer; + } } } else { // We always want to animate the non fullscreen windows, they don't share their diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index fbb826d5cb48..b79173cf77e5 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -31,11 +31,13 @@ import android.graphics.GraphicBuffer; import android.graphics.Rect; import android.os.Environment; import android.util.ArraySet; +import android.view.WindowManager.LayoutParams; import android.view.WindowManagerPolicy.StartingSurface; import com.google.android.collect.Sets; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.wm.TaskSnapshotSurface.SystemBarBackgroundPainter; import java.io.PrintWriter; @@ -206,7 +208,7 @@ class TaskSnapshotController { final AppWindowToken topChild = task.getTopChild(); if (StackId.isHomeOrRecentsStack(task.mStack.mStackId)) { return SNAPSHOT_MODE_NONE; - } else if (topChild != null && topChild.shouldDisablePreviewScreenshots()) { + } else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) { return SNAPSHOT_MODE_APP_THEME; } else { return SNAPSHOT_MODE_REAL; @@ -227,6 +229,8 @@ class TaskSnapshotController { return null; } final int color = task.getTaskDescription().getBackgroundColor(); + final int statusBarColor = task.getTaskDescription().getStatusBarColor(); + final int navigationBarColor = task.getTaskDescription().getNavigationBarColor(); final GraphicBuffer buffer = GraphicBuffer.create(mainWindow.getFrameLw().width(), mainWindow.getFrameLw().height(), RGBA_8888, USAGE_HW_TEXTURE | USAGE_SW_WRITE_RARELY | USAGE_SW_READ_NEVER); @@ -235,6 +239,11 @@ class TaskSnapshotController { } final Canvas c = buffer.lockCanvas(); c.drawColor(color); + final LayoutParams attrs = mainWindow.getAttrs(); + final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags, + attrs.privateFlags, attrs.systemUiVisibility, statusBarColor, navigationBarColor); + decorPainter.setInsets(mainWindow.mContentInsets, mainWindow.mStableInsets); + decorPainter.drawDecors(c, null /* statusBarExcludeFrame */); buffer.unlockCanvasAndPost(c); return new TaskSnapshot(buffer, topChild.getConfiguration().orientation, mainWindow.mStableInsets, false /* reduced */, 1.0f /* scale */); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java index f2a92df2b820..e5c7a72c78bc 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java @@ -24,6 +24,7 @@ import android.annotation.TestApi; import android.app.ActivityManager.TaskSnapshot; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; +import android.graphics.Bitmap.Config; import android.os.Process; import android.os.SystemClock; import android.util.ArraySet; @@ -266,12 +267,13 @@ class TaskSnapshotPersister { final File file = getBitmapFile(mTaskId, mUserId); final File reducedFile = getReducedResolutionBitmapFile(mTaskId, mUserId); final Bitmap bitmap = Bitmap.createHardwareBitmap(mSnapshot.getSnapshot()); - final Bitmap reduced = Bitmap.createScaledBitmap(bitmap, + final Bitmap swBitmap = bitmap.copy(Config.ARGB_8888, false /* isMutable */); + final Bitmap reduced = Bitmap.createScaledBitmap(swBitmap, (int) (bitmap.getWidth() * REDUCED_SCALE), (int) (bitmap.getHeight() * REDUCED_SCALE), true /* filter */); try { FileOutputStream fos = new FileOutputStream(file); - bitmap.compress(JPEG, QUALITY, fos); + swBitmap.compress(JPEG, QUALITY, fos); fos.close(); FileOutputStream reducedFos = new FileOutputStream(reducedFile); reduced.compress(JPEG, QUALITY, reducedFos); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index c816ba3fa796..2b9e800fd854 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -42,8 +42,11 @@ import static com.android.internal.policy.DecorView.getNavigationBarRect; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.annotation.Nullable; import android.app.ActivityManager.TaskDescription; import android.app.ActivityManager.TaskSnapshot; +import android.app.ActivityThread; +import android.content.Context; import android.graphics.Canvas; import android.graphics.GraphicBuffer; import android.graphics.Paint; @@ -118,13 +121,8 @@ class TaskSnapshotSurface implements StartingSurface { private final Handler mHandler; private boolean mSizeMismatch; private final Paint mBackgroundPaint = new Paint(); - private final Paint mStatusBarPaint = new Paint(); - private final Paint mNavigationBarPaint = new Paint(); private final int mStatusBarColor; - private final int mNavigationBarColor; - private final int mSysUiVis; - private final int mWindowFlags; - private final int mWindowPrivateFlags; + @VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter; static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token, TaskSnapshot snapshot) { @@ -224,15 +222,9 @@ class TaskSnapshotSurface implements StartingSurface { mTitle = title; mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE); mTaskBounds = taskBounds; - mSysUiVis = sysUiVis; - mWindowFlags = windowFlags; - mWindowPrivateFlags = windowPrivateFlags; - mStatusBarColor = DecorView.calculateStatusBarColor(windowFlags, - service.mContext.getColor(R.color.system_bar_background_semi_transparent), - statusBarColor); - mNavigationBarColor = navigationBarColor; - mStatusBarPaint.setColor(mStatusBarColor); - mNavigationBarPaint.setColor(navigationBarColor); + mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags, + windowPrivateFlags, sysUiVis, statusBarColor, navigationBarColor); + mStatusBarColor = statusBarColor; } @Override @@ -258,6 +250,7 @@ class TaskSnapshotSurface implements StartingSurface { mStableInsets.set(stableInsets); mSizeMismatch = (mFrame.width() != mSnapshot.getSnapshot().getWidth() || mFrame.height() != mSnapshot.getSnapshot().getHeight()); + mSystemBarBackgroundPainter.setInsets(contentInsets, stableInsets); } private void drawSnapshot() { @@ -346,7 +339,7 @@ class TaskSnapshotSurface implements StartingSurface { @VisibleForTesting void drawBackgroundAndBars(Canvas c, Rect frame) { - final int statusBarHeight = getStatusBarColorViewHeight(); + final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight(); final boolean fillHorizontally = c.getWidth() > frame.right; final boolean fillVertically = c.getHeight() > frame.bottom; if (fillHorizontally) { @@ -359,44 +352,7 @@ class TaskSnapshotSurface implements StartingSurface { if (fillVertically) { c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint); } - drawStatusBarBackground(c, frame, statusBarHeight); - drawNavigationBarBackground(c); - } - - private int getStatusBarColorViewHeight() { - final boolean forceStatusBarBackground = - (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0; - if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( - mSysUiVis, mStatusBarColor, mWindowFlags, forceStatusBarBackground)) { - return getColorViewTopInset(mStableInsets.top, mContentInsets.top); - } else { - return 0; - } - } - - private boolean isNavigationBarColorViewVisible() { - return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( - mSysUiVis, mNavigationBarColor, mWindowFlags, false /* force */); - } - - @VisibleForTesting - void drawStatusBarBackground(Canvas c, Rect frame, int statusBarHeight) { - if (statusBarHeight > 0 && c.getWidth() > frame.right) { - final int rightInset = DecorView.getColorViewRightInset(mStableInsets.right, - mContentInsets.right); - c.drawRect(frame.right, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint); - } - } - - @VisibleForTesting - void drawNavigationBarBackground(Canvas c) { - final Rect navigationBarRect = new Rect(); - getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets, - navigationBarRect); - final boolean visible = isNavigationBarColorViewVisible(); - if (visible && !navigationBarRect.isEmpty()) { - c.drawRect(navigationBarRect, mNavigationBarPaint); - } + mSystemBarBackgroundPainter.drawDecors(c, frame); } private void reportDrawn() { @@ -450,4 +406,84 @@ class TaskSnapshotSurface implements StartingSurface { } } } + + /** + * Helper class to draw the background of the system bars in regions the task snapshot isn't + * filling the window. + */ + static class SystemBarBackgroundPainter { + + private final Rect mContentInsets = new Rect(); + private final Rect mStableInsets = new Rect(); + private final Paint mStatusBarPaint = new Paint(); + private final Paint mNavigationBarPaint = new Paint(); + private final int mStatusBarColor; + private final int mNavigationBarColor; + private final int mWindowFlags; + private final int mWindowPrivateFlags; + private final int mSysUiVis; + + SystemBarBackgroundPainter( int windowFlags, int windowPrivateFlags, int sysUiVis, + int statusBarColor, int navigationBarColor) { + mWindowFlags = windowFlags; + mWindowPrivateFlags = windowPrivateFlags; + mSysUiVis = sysUiVis; + final Context context = ActivityThread.currentActivityThread().getSystemUiContext(); + mStatusBarColor = DecorView.calculateStatusBarColor(windowFlags, + context.getColor(R.color.system_bar_background_semi_transparent), + statusBarColor); + mNavigationBarColor = navigationBarColor; + mStatusBarPaint.setColor(mStatusBarColor); + mNavigationBarPaint.setColor(navigationBarColor); + } + + void setInsets(Rect contentInsets, Rect stableInsets) { + mContentInsets.set(contentInsets); + mStableInsets.set(stableInsets); + } + + int getStatusBarColorViewHeight() { + final boolean forceStatusBarBackground = + (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0; + if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( + mSysUiVis, mStatusBarColor, mWindowFlags, forceStatusBarBackground)) { + return getColorViewTopInset(mStableInsets.top, mContentInsets.top); + } else { + return 0; + } + } + + private boolean isNavigationBarColorViewVisible() { + return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( + mSysUiVis, mNavigationBarColor, mWindowFlags, false /* force */); + } + + void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) { + drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight()); + drawNavigationBarBackground(c); + } + + @VisibleForTesting + void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame, + int statusBarHeight) { + if (statusBarHeight > 0 + && (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) { + final int rightInset = DecorView.getColorViewRightInset(mStableInsets.right, + mContentInsets.right); + final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0; + c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint); + } + } + + @VisibleForTesting + void drawNavigationBarBackground(Canvas c) { + final Rect navigationBarRect = new Rect(); + getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets, + navigationBarRect); + final boolean visible = isNavigationBarColorViewVisible(); + if (visible && !navigationBarRect.isEmpty()) { + c.drawRect(navigationBarRect, mNavigationBarPaint); + } + } + } } diff --git a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java index 0cf4994ebbce..2e8b0682f53d 100644 --- a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java +++ b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java @@ -69,7 +69,7 @@ public class BadgeExtractorTest { Notification n = builder.build(); StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid, mPid, n, mUser, null, System.currentTimeMillis()); - NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); + NotificationRecord r = new NotificationRecord(getContext(), sbn, channel, true); return r; } diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java index d4904f5aecf7..5e8b3d43682b 100644 --- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -259,7 +259,7 @@ public class BuzzBeepBlinkTest { StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid, mPid, n, mUser, null, System.currentTimeMillis()); - NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); + NotificationRecord r = new NotificationRecord(getContext(), sbn, channel, true); mService.addNotification(r); return r; } @@ -769,7 +769,7 @@ public class BuzzBeepBlinkTest { StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 0, mTag, mUid, mPid, n, mUser, null, System.currentTimeMillis()); - NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); + NotificationRecord r = new NotificationRecord(getContext(), sbn, channel, true); mService.addNotification(r); mService.buzzBeepBlinkLocked(r); diff --git a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java index 24cb72e8b0fb..33d2d079d87e 100644 --- a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java +++ b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java @@ -53,21 +53,21 @@ public class GlobalSortKeyComparatorTest { new StatusBarNotification(PKG, PKG, 1, "media", UID, UID, n, new UserHandle(UserHandle.myUserId()), - "", 1499), getDefaultChannel()); + "", 1499), getDefaultChannel(), true); left.setGlobalSortKey("first"); NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(), new StatusBarNotification(PKG, PKG, 1, "media", UID, UID, n, new UserHandle(UserHandle.myUserId()), - "", 1499), getDefaultChannel()); + "", 1499), getDefaultChannel(), true); right.setGlobalSortKey("second"); NotificationRecord last = new NotificationRecord(InstrumentationRegistry.getContext(), new StatusBarNotification(PKG, PKG, 1, "media", UID, UID, n, new UserHandle(UserHandle.myUserId()), - "", 1499), getDefaultChannel()); + "", 1499), getDefaultChannel(), true); final List<NotificationRecord> expected = new ArrayList<>(); @@ -93,13 +93,13 @@ public class GlobalSortKeyComparatorTest { new StatusBarNotification(PKG, PKG, 1, "media", UID, UID, n, new UserHandle(UserHandle.myUserId()), - "", 1499), getDefaultChannel()); + "", 1499), getDefaultChannel(), true); NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(), new StatusBarNotification(PKG, PKG, 1, "media", UID, UID, n, new UserHandle(UserHandle.myUserId()), - "", 1499), getDefaultChannel()); + "", 1499), getDefaultChannel(), true); right.setGlobalSortKey("not null"); final List<NotificationRecord> expected = new ArrayList<>(); @@ -124,14 +124,14 @@ public class GlobalSortKeyComparatorTest { new StatusBarNotification(PKG, PKG, 1, "media", UID, UID, n, new UserHandle(UserHandle.myUserId()), - "", 1499), getDefaultChannel()); + "", 1499), getDefaultChannel(), true); left.setGlobalSortKey("not null"); NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(), new StatusBarNotification(PKG, PKG, 1, "media", UID, UID, n, new UserHandle(UserHandle.myUserId()), - "", 1499), getDefaultChannel()); + "", 1499), getDefaultChannel(), true); final List<NotificationRecord> expected = new ArrayList<>(); expected.add(left); diff --git a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java index 3dbd803e0a76..7a2dbaf98692 100644 --- a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java +++ b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java @@ -71,7 +71,7 @@ public class ImportanceExtractorTest { Notification n = builder.build(); StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid, mPid, n, mUser, null, System.currentTimeMillis()); - NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); + NotificationRecord r = new NotificationRecord(getContext(), sbn, channel, true); return r; } diff --git a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java index 84945ab5acd3..ccd2db0ebf2c 100644 --- a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java +++ b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java @@ -108,7 +108,7 @@ public class NotificationComparatorTest { .build(); mRecordMinCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg, callPkg, 1, "minCall", callUid, callUid, n1, - new UserHandle(userId), "", 2000), getDefaultChannel()); + new UserHandle(userId), "", 2000), getDefaultChannel(), false); mRecordMinCall.setUserImportance(NotificationManager.IMPORTANCE_MIN); Notification n2 = new Notification.Builder(mContext, TEST_CHANNEL_ID) @@ -118,7 +118,7 @@ public class NotificationComparatorTest { .build(); mRecordHighCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg, callPkg, 1, "highcall", callUid, callUid, n2, - new UserHandle(userId), "", 1999), getDefaultChannel()); + new UserHandle(userId), "", 1999), getDefaultChannel(), false); mRecordHighCall.setUserImportance(NotificationManager.IMPORTANCE_HIGH); Notification n3 = new Notification.Builder(mContext, TEST_CHANNEL_ID) @@ -128,14 +128,14 @@ public class NotificationComparatorTest { .build(); mRecordDefaultMedia = new NotificationRecord(mContext, new StatusBarNotification(pkg2, pkg2, 1, "media", uid2, uid2, n3, new UserHandle(userId), - "", 1499), getDefaultChannel()); + "", 1499), getDefaultChannel(), false); mRecordDefaultMedia.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT); Notification n4 = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setStyle(new Notification.MessagingStyle("sender!")).build(); mRecordInlineReply = new NotificationRecord(mContext, new StatusBarNotification(pkg2, pkg2, 1, "inlinereply", uid2, uid2, n4, new UserHandle(userId), - "", 1599), getDefaultChannel()); + "", 1599), getDefaultChannel(), false); mRecordInlineReply.setUserImportance(NotificationManager.IMPORTANCE_HIGH); mRecordInlineReply.setPackagePriority(Notification.PRIORITY_MAX); @@ -143,27 +143,27 @@ public class NotificationComparatorTest { .setCategory(Notification.CATEGORY_MESSAGE).build(); mRecordSms = new NotificationRecord(mContext, new StatusBarNotification(smsPkg, smsPkg, 1, "sms", smsUid, smsUid, n5, new UserHandle(userId), - "", 1299), getDefaultChannel()); + "", 1299), getDefaultChannel(), false); mRecordSms.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT); Notification n6 = new Notification.Builder(mContext, TEST_CHANNEL_ID).build(); mRecordStarredContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2, pkg2, 1, "starred", uid2, uid2, n6, new UserHandle(userId), - "", 1259), getDefaultChannel()); + "", 1259), getDefaultChannel(), false); mRecordStarredContact.setContactAffinity(ValidateNotificationPeople.STARRED_CONTACT); mRecordStarredContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT); Notification n7 = new Notification.Builder(mContext, TEST_CHANNEL_ID).build(); mRecordContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2, pkg2, 1, "contact", uid2, uid2, n7, new UserHandle(userId), - "", 1259), getDefaultChannel()); + "", 1259), getDefaultChannel(), false); mRecordContact.setContactAffinity(ValidateNotificationPeople.VALID_CONTACT); mRecordContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT); Notification n8 = new Notification.Builder(mContext, TEST_CHANNEL_ID).build(); mRecordUrgent = new NotificationRecord(mContext, new StatusBarNotification(pkg2, pkg2, 1, "urgent", uid2, uid2, n8, new UserHandle(userId), - "", 1258), getDefaultChannel()); + "", 1258), getDefaultChannel(), false); mRecordUrgent.setUserImportance(NotificationManager.IMPORTANCE_HIGH); Notification n9 = new Notification.Builder(mContext, TEST_CHANNEL_ID) @@ -173,7 +173,7 @@ public class NotificationComparatorTest { .build(); mRecordCheater = new NotificationRecord(mContext, new StatusBarNotification(pkg2, pkg2, 1, "cheater", uid2, uid2, n9, new UserHandle(userId), - "", 9258), getDefaultChannel()); + "", 9258), getDefaultChannel(), false); mRecordCheater.setUserImportance(NotificationManager.IMPORTANCE_LOW); mRecordCheater.setPackagePriority(Notification.PRIORITY_MAX); @@ -181,7 +181,7 @@ public class NotificationComparatorTest { .setStyle(new Notification.InboxStyle().setSummaryText("message!")).build(); mRecordEmail = new NotificationRecord(mContext, new StatusBarNotification(pkg2, pkg2, 1, "email", uid2, uid2, n10, new UserHandle(userId), - "", 1599), getDefaultChannel()); + "", 1599), getDefaultChannel(), false); mRecordEmail.setUserImportance(NotificationManager.IMPORTANCE_HIGH); Notification n11 = new Notification.Builder(mContext, TEST_CHANNEL_ID) @@ -190,7 +190,7 @@ public class NotificationComparatorTest { .build(); mRecordCheaterColorized = new NotificationRecord(mContext, new StatusBarNotification(pkg2, pkg2, 1, "cheater", uid2, uid2, n11, new UserHandle(userId), - "", 9258), getDefaultChannel()); + "", 9258), getDefaultChannel(), false); mRecordCheaterColorized.setUserImportance(NotificationManager.IMPORTANCE_LOW); } diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java index 177c02d63280..f3eb4f817262 100644 --- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -165,7 +165,7 @@ public class NotificationManagerServiceTest { StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id, "tag", uid, 0, nb.build(), new UserHandle(uid), null, 0); - return new NotificationRecord(mContext, sbn, channel); + return new NotificationRecord(mContext, sbn, channel, true); } private NotificationRecord generateNotificationRecord(NotificationChannel channel) { return generateNotificationRecord(channel, null); @@ -184,7 +184,7 @@ public class NotificationManagerServiceTest { } StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", uid, 0, nb.build(), new UserHandle(uid), null, 0); - return new NotificationRecord(mContext, sbn, channel); + return new NotificationRecord(mContext, sbn, channel, true); } @Test diff --git a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java index 1c8ca84815d3..66f379970d5a 100644 --- a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java +++ b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java @@ -21,7 +21,6 @@ import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; -import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.when; @@ -61,6 +60,10 @@ public class NotificationRecordTest { private final Context mMockContext = Mockito.mock(Context.class); @Mock PackageManager mPm; + // constants for targetSdk version. N is pre channels, O is post. + private final boolean N = false; + private final boolean O = true; + private final String pkg = "com.android.server.notification"; private final int uid = 9583; private final String pkg2 = "pkg2"; @@ -76,7 +79,6 @@ public class NotificationRecordTest { new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "test", NotificationManager.IMPORTANCE_UNSPECIFIED); private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser()); - final ApplicationInfo legacy = new ApplicationInfo(); final ApplicationInfo upgrade = new ApplicationInfo(); private static final long[] CUSTOM_VIBRATION = new long[] { @@ -100,17 +102,13 @@ public class NotificationRecordTest { InstrumentationRegistry.getContext().getResources()); when(mMockContext.getPackageManager()).thenReturn(mPm); - legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1; upgrade.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1; - try { - when(mPm.getApplicationInfoAsUser(eq(pkg), anyInt(), anyInt())).thenReturn(legacy); - when(mPm.getApplicationInfoAsUser(eq(pkg2), anyInt(), anyInt())).thenReturn(upgrade); - } catch (PackageManager.NameNotFoundException e) {} + when(mMockContext.getApplicationInfo()).thenReturn(upgrade); } - private StatusBarNotification getNotification(boolean preO, boolean noisy, boolean defaultSound, - boolean buzzy, boolean defaultVibration, boolean lights, boolean defaultLights) { - when(mMockContext.getApplicationInfo()).thenReturn(preO ? legacy : upgrade); + private StatusBarNotification getNotification(boolean supportsChannels, boolean noisy, + boolean defaultSound, boolean buzzy, boolean defaultVibration, boolean lights, + boolean defaultLights) { final Builder builder = new Builder(mMockContext) .setContentTitle("foo") .setSmallIcon(android.R.drawable.sym_def_app_icon) @@ -149,13 +147,13 @@ public class NotificationRecordTest { } builder.setDefaults(defaults); - if (!preO) { + if (supportsChannels) { builder.setChannelId(channelId); } Notification n = builder.build(); - if (preO) { + if (!supportsChannels) { return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid); } else { @@ -172,11 +170,11 @@ public class NotificationRecordTest { public void testSound_default_preUpgradeUsesNotification() throws Exception { defaultChannel.setSound(null, null); // pre upgrade, default sound. - StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */, + StatusBarNotification sbn = getNotification(N, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, false /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N); assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, record.getSound()); assertEquals(Notification.AUDIO_ATTRIBUTES_DEFAULT, record.getAudioAttributes()); } @@ -185,11 +183,11 @@ public class NotificationRecordTest { public void testSound_custom_preUpgradeUsesNotification() throws Exception { defaultChannel.setSound(null, null); // pre upgrade, custom sound. - StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */, + StatusBarNotification sbn = getNotification(N, true /* noisy */, false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, false /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N); assertEquals(CUSTOM_SOUND, record.getSound()); assertEquals(CUSTOM_ATTRIBUTES, record.getAudioAttributes()); } @@ -199,11 +197,11 @@ public class NotificationRecordTest { defaultChannel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES); defaultChannel.lockFields(NotificationChannel.USER_LOCKED_SOUND); // pre upgrade, default sound. - StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */, + StatusBarNotification sbn = getNotification(N, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, false /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N); assertEquals(CUSTOM_SOUND, record.getSound()); assertEquals(CUSTOM_ATTRIBUTES, record.getAudioAttributes()); } @@ -211,11 +209,11 @@ public class NotificationRecordTest { @Test public void testSound_noSound_preUpgrade() throws Exception { // pre upgrade, default sound. - StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */, + StatusBarNotification sbn = getNotification(N, false /* noisy */, false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, false /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N); assertEquals(null, record.getSound()); assertEquals(Notification.AUDIO_ATTRIBUTES_DEFAULT, record.getAudioAttributes()); } @@ -224,11 +222,11 @@ public class NotificationRecordTest { public void testSound_default_upgradeUsesChannel() throws Exception { channel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES); // post upgrade, default sound. - StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */, + StatusBarNotification sbn = getNotification(O, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, false /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel, O); assertEquals(CUSTOM_SOUND, record.getSound()); assertEquals(CUSTOM_ATTRIBUTES, record.getAudioAttributes()); } @@ -237,11 +235,11 @@ public class NotificationRecordTest { public void testVibration_default_preUpgradeUsesNotification() throws Exception { defaultChannel.enableVibration(false); // pre upgrade, default vibration. - StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */, + StatusBarNotification sbn = getNotification(N, false /* noisy */, false /* defaultSound */, true /* buzzy */, true /* defaultBuzz */, false /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N); assertNotNull(record.getVibration()); } @@ -249,11 +247,11 @@ public class NotificationRecordTest { public void testVibration_custom_preUpgradeUsesNotification() throws Exception { defaultChannel.enableVibration(false); // pre upgrade, custom vibration. - StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */, + StatusBarNotification sbn = getNotification(N, false /* noisy */, false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */, false /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N); assertEquals(CUSTOM_VIBRATION, record.getVibration()); } @@ -262,11 +260,11 @@ public class NotificationRecordTest { defaultChannel.enableVibration(true); defaultChannel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION); // pre upgrade, custom vibration. - StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */, + StatusBarNotification sbn = getNotification(N, false /* noisy */, false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */, false /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N); assertTrue(!Objects.equals(CUSTOM_VIBRATION, record.getVibration())); } @@ -274,20 +272,20 @@ public class NotificationRecordTest { public void testVibration_custom_upgradeUsesChannel() throws Exception { channel.enableVibration(true); // post upgrade, custom vibration. - StatusBarNotification sbn = getNotification(false /*preO */, false /* noisy */, + StatusBarNotification sbn = getNotification(O, false /* noisy */, false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */, false /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel, O); assertEquals(CUSTOM_CHANNEL_VIBRATION, record.getVibration()); } @Test public void testImportance_preUpgrade() throws Exception { - StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */, + StatusBarNotification sbn = getNotification(N, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, false /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N); assertEquals(NotificationManager.IMPORTANCE_HIGH, record.getImportance()); } @@ -295,11 +293,11 @@ public class NotificationRecordTest { public void testImportance_locked_preUpgrade() throws Exception { defaultChannel.setImportance(NotificationManager.IMPORTANCE_LOW); defaultChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); - StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */, + StatusBarNotification sbn = getNotification(N, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, false /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N); assertEquals(NotificationManager.IMPORTANCE_LOW, record.getImportance()); } @@ -307,39 +305,39 @@ public class NotificationRecordTest { public void testImportance_locked_unspecified_preUpgrade() throws Exception { defaultChannel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED); defaultChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); - StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */, + StatusBarNotification sbn = getNotification(N, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, false /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N); assertEquals(NotificationManager.IMPORTANCE_HIGH, record.getImportance()); } @Test public void testImportance_upgrade() throws Exception { - StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */, + StatusBarNotification sbn = getNotification(O, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, false /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel, O); assertEquals(NotificationManager.IMPORTANCE_DEFAULT, record.getImportance()); } @Test public void testLights_preUpgrade_noLight() throws Exception { - StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */, + StatusBarNotification sbn = getNotification(N, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, false /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N); assertNull(record.getLight()); } @Test public void testLights_preUpgrade() throws Exception { - StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */, + StatusBarNotification sbn = getNotification(N, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, true /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N); assertEquals(CUSTOM_LIGHT, record.getLight()); } @@ -347,11 +345,11 @@ public class NotificationRecordTest { public void testLights_locked_preUpgrade() throws Exception { defaultChannel.enableLights(true); defaultChannel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS); - StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */, + StatusBarNotification sbn = getNotification(N, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, true /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N); assertFalse(CUSTOM_LIGHT.equals(record.getLight())); } @@ -366,10 +364,10 @@ public class NotificationRecordTest { NotificationRecord.Light expected = new NotificationRecord.Light( defaultLightColor, defaultLightOn, defaultLightOff); - StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */, + StatusBarNotification sbn = getNotification(O, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, true /* lights */, true /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel, O); assertEquals(expected, record.getLight()); } @@ -382,19 +380,19 @@ public class NotificationRecordTest { NotificationRecord.Light expected = new NotificationRecord.Light( Color.BLUE, defaultLightOn, defaultLightOff); - StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */, + StatusBarNotification sbn = getNotification(O, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, true /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel, O); assertEquals(expected, record.getLight()); } @Test public void testLights_upgrade_noLight() throws Exception { - StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */, + StatusBarNotification sbn = getNotification(O, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, false /* lights */, false /*defaultLights */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, O); assertNull(record.getLight()); } } diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java index c48e73879b13..8ab6d08c3e4f 100644 --- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java +++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java @@ -124,7 +124,7 @@ public class RankingHelperTest { .build(); mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification( "package", "package", 1, null, 0, 0, mNotiGroupGSortA, user, - null, System.currentTimeMillis()), getDefaultChannel()); + null, System.currentTimeMillis()), getDefaultChannel(), false); mNotiGroupGSortB = new Notification.Builder(getContext(), TEST_CHANNEL_ID) .setContentTitle("B") @@ -134,7 +134,7 @@ public class RankingHelperTest { .build(); mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification( "package", "package", 1, null, 0, 0, mNotiGroupGSortB, user, - null, System.currentTimeMillis()), getDefaultChannel()); + null, System.currentTimeMillis()), getDefaultChannel(), false); mNotiNoGroup = new Notification.Builder(getContext(), TEST_CHANNEL_ID) .setContentTitle("C") @@ -142,7 +142,7 @@ public class RankingHelperTest { .build(); mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification( "package", "package", 1, null, 0, 0, mNotiNoGroup, user, - null, System.currentTimeMillis()), getDefaultChannel()); + null, System.currentTimeMillis()), getDefaultChannel(), false); mNotiNoGroup2 = new Notification.Builder(getContext(), TEST_CHANNEL_ID) .setContentTitle("D") @@ -150,7 +150,7 @@ public class RankingHelperTest { .build(); mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification( "package", "package", 1, null, 0, 0, mNotiNoGroup2, user, - null, System.currentTimeMillis()), getDefaultChannel()); + null, System.currentTimeMillis()), getDefaultChannel(), false); mNotiNoGroupSortA = new Notification.Builder(getContext(), TEST_CHANNEL_ID) .setContentTitle("E") @@ -159,7 +159,7 @@ public class RankingHelperTest { .build(); mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification( "package", "package", 1, null, 0, 0, mNotiNoGroupSortA, user, - null, System.currentTimeMillis()), getDefaultChannel()); + null, System.currentTimeMillis()), getDefaultChannel(), false); mAudioAttributes = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) @@ -373,7 +373,7 @@ public class RankingHelperTest { assertNull(mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false)); assertNull(mHelper.getNotificationChannel(PKG, UID, channel3.getId(), false)); assertNull(mHelper.getNotificationChannelGroup(ncg.getId(), PKG, UID)); - //assertEquals(ncg2, mHelper.getNotificationChannelGroup(ncg2.getId(), PKG, UID)); + assertEquals(ncg2, mHelper.getNotificationChannelGroup(ncg2.getId(), PKG, UID)); assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false)); } @@ -696,20 +696,14 @@ public class RankingHelperTest { // Returns only non-deleted channels List<NotificationChannel> channels = mHelper.getNotificationChannels(PKG, UID, false).getList(); - assertEquals(2, channels.size()); // Default channel + non-deleted channel - for (NotificationChannel nc : channels) { - if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) { - compareChannels(channel2, nc); - } - } + assertEquals(1, channels.size()); + compareChannels(channel2, channels.get(0)); // Returns deleted channels too channels = mHelper.getNotificationChannels(PKG, UID, true).getList(); - assertEquals(3, channels.size()); // Includes default channel + assertEquals(2, channels.size()); for (NotificationChannel nc : channels) { - if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) { - compareChannels(channelMap.get(nc.getId()), nc); - } + compareChannels(channelMap.get(nc.getId()), nc); } } @@ -807,8 +801,8 @@ public class RankingHelperTest { mHelper.permanentlyDeleteNotificationChannels(PKG, UID); - // Only default channel remains - assertEquals(1, mHelper.getNotificationChannels(PKG, UID, true).getList().size()); + // No channels remain + assertEquals(0, mHelper.getNotificationChannels(PKG, UID, true).getList().size()); } @Test @@ -849,6 +843,36 @@ public class RankingHelperTest { } @Test + public void testOnUserRemoved() throws Exception { + int[] user0Uids = {98, 235, 16, 3782}; + int[] user1Uids = new int[user0Uids.length]; + for (int i = 0; i < user0Uids.length; i++) { + user1Uids[i] = UserHandle.PER_USER_RANGE + user0Uids[i]; + + final ApplicationInfo legacy = new ApplicationInfo(); + legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1; + when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy); + + // create records with the default channel for all user 0 and user 1 uids + mHelper.getImportance(PKG, user0Uids[i]); + mHelper.getImportance(PKG, user1Uids[i]); + } + + mHelper.onUserRemoved(1); + + // user 0 records remain + for (int i = 0; i < user0Uids.length; i++) { + assertEquals(1, + mHelper.getNotificationChannels(PKG, user0Uids[i], false).getList().size()); + } + // user 1 records are gone + for (int i = 0; i < user1Uids.length; i++) { + assertEquals(0, + mHelper.getNotificationChannels(PKG, user1Uids[i], false).getList().size()); + } + } + + @Test public void testOnPackageChanged_packageRemoval() throws Exception { // Deleted NotificationChannel channel1 = @@ -857,13 +881,32 @@ public class RankingHelperTest { mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID}); - assertEquals(0, mHelper.getNotificationChannels(PKG, UID, true).getList().size()); + // since this is a pre upgrade app, clearing data should restore the default channel + assertEquals(1, mHelper.getNotificationChannels(PKG, UID, true).getList().size()); + assertEquals(NotificationChannel.DEFAULT_CHANNEL_ID, + mHelper.getNotificationChannels(PKG, UID, true).getList().get(0).getId()); // Not deleted mHelper.createNotificationChannel(PKG, UID, channel1, true); - mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID}); - assertEquals(2, mHelper.getNotificationChannels(PKG, UID, false).getList().size()); + assertEquals(1, mHelper.getNotificationChannels(PKG, UID, false).getList().size()); + } + + @Test + public void testOnPackageChanged_packageRemoval_updatedPackage() throws Exception { + // Deleted + NotificationChannel channel1 = + new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH); + mHelper.createNotificationChannel(UPDATED_PKG, UID2, channel1, true); + mHelper.onPackagesChanged( + true, UserHandle.USER_SYSTEM, new String[]{UPDATED_PKG}, new int[]{UID2}); + assertEquals(0, mHelper.getNotificationChannels(UPDATED_PKG, UID2, true).getList().size()); + + // Not deleted + mHelper.createNotificationChannel(UPDATED_PKG, UID2, channel1, true); + mHelper.onPackagesChanged( + false, UserHandle.USER_SYSTEM, new String[]{UPDATED_PKG}, new int[]{UID2}); + assertEquals(1, mHelper.getNotificationChannels(UPDATED_PKG, UID2, false).getList().size()); } @Test @@ -884,7 +927,23 @@ public class RankingHelperTest { mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID}); - assertEquals(0, mHelper.getNotificationChannelGroups(PKG, UID, true).getList().size()); + // default channel restored + assertEquals(1, mHelper.getNotificationChannelGroups(PKG, UID, true).getList().size()); + assertNull(mHelper.getNotificationChannelGroups(PKG, UID, true).getList().get(0).getId()); + } + + @Test + public void testOnPackageChanged_packageRemoval_groups_upgraded() throws Exception { + NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1"); + mHelper.createNotificationChannelGroup(UPDATED_PKG, UID2, ncg, true); + NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2"); + mHelper.createNotificationChannelGroup(UPDATED_PKG, UID2, ncg2, true); + + mHelper.onPackagesChanged( + true, UserHandle.USER_SYSTEM, new String[]{UPDATED_PKG}, new int[]{UID2}); + + assertEquals(0, + mHelper.getNotificationChannelGroups(UPDATED_PKG, UID2, true).getList().size()); } @Test @@ -958,9 +1017,8 @@ public class RankingHelperTest { assertEquals(3, actual.size()); for (NotificationChannelGroup group : actual) { if (group.getId() == null) { - assertEquals(2, group.getChannels().size()); // misc channel too - assertTrue(channel3.getId().equals(group.getChannels().get(0).getId()) - || channel3.getId().equals(group.getChannels().get(1).getId())); + assertEquals(1, group.getChannels().size()); + assertTrue(channel3.getId().equals(group.getChannels().get(0).getId())); } else if (group.getId().equals(ncg.getId())) { assertEquals(2, group.getChannels().size()); if (group.getChannels().get(0).getId().equals(channel1.getId())) { @@ -994,12 +1052,8 @@ public class RankingHelperTest { List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(PKG, UID, true).getList(); - assertEquals(2, actual.size()); - for (NotificationChannelGroup group : actual) { - if (Objects.equals(group.getId(), ncg.getId())) { - assertEquals(1, group.getChannels().size()); - } - } + assertEquals(1, actual.size()); + assertEquals(1, actual.get(0).getChannels().size()); } @Test diff --git a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java index bc25860e8e92..b1cb4d7d4e67 100644 --- a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java @@ -312,7 +312,7 @@ public class SnoozeHelperTest { TEST_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_LOW); return new NotificationRecord(getContext(), new StatusBarNotification( pkg, pkg, id, tag, 0, 0, n, user, null, - System.currentTimeMillis()), notificationChannel); + System.currentTimeMillis()), notificationChannel, true); } private NotificationRecord getNotificationRecord(String pkg, int id, String tag, diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index cc682c4ee985..fa72416edd16 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -18,6 +18,7 @@ package="com.android.frameworks.servicestests"> <uses-permission android:name="android.permission.READ_LOGS" /> + <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" /> <uses-permission android:name="android.permission.ACCOUNT_MANAGER" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java index c78488f9d8d9..bb7e20bcb9c3 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java @@ -368,8 +368,8 @@ public class NetworkScoreServiceTest { } @Test - public void testSetActiveScorer_noRequestNetworkScoresPermission() { - when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) + public void testSetActiveScorer_noScoreNetworksPermission() { + when(mContext.checkCallingOrSelfPermission(permission.SCORE_NETWORKS)) .thenReturn(PackageManager.PERMISSION_DENIED); try { diff --git a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java index 0694eaefd288..ceb92dece764 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java @@ -228,30 +228,23 @@ public class NetworkScorerAppManagerTest { } @Test - public void testSetActiveScorer_nullPackage_validDefault() throws Exception { - String packageName = "package"; - String defaultPackage = "defaultPackage"; - setNetworkRecoPackageSetting(packageName); - setDefaultNetworkRecommendationPackage(defaultPackage); - final ComponentName recoComponent = new ComponentName(defaultPackage, "class1"); - mockScoreNetworksGranted(recoComponent.getPackageName()); - mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */, null); + public void testSetActiveScorer_nullPackage_currentIsSet() throws Exception { + setNetworkRecoPackageSetting("package"); assertTrue(mNetworkScorerAppManager.setActiveScorer(null)); verify(mSettingsFacade).putString(mMockContext, - Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE, defaultPackage); + Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE, null); + verify(mSettingsFacade).putInt(mMockContext, + Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, + NetworkScoreManager.RECOMMENDATIONS_ENABLED_FORCED_OFF); } @Test - public void testSetActiveScorer_nullPackage_invalidDefault() throws Exception { - String packageName = "package"; - String defaultPackage = "defaultPackage"; - setNetworkRecoPackageSetting(packageName); - setDefaultNetworkRecommendationPackage(defaultPackage); + public void testSetActiveScorer_nullPackage_currentIsNull() throws Exception { + setNetworkRecoPackageSetting(null); - assertFalse(mNetworkScorerAppManager.setActiveScorer(null)); - verify(mSettingsFacade, never()).putString(any(), - eq(Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE), any()); + assertTrue(mNetworkScorerAppManager.setActiveScorer(null)); + verify(mSettingsFacade, never()).putString(any(), any(), any()); } @Test @@ -266,6 +259,9 @@ public class NetworkScorerAppManagerTest { assertTrue(mNetworkScorerAppManager.setActiveScorer(newPackage)); verify(mSettingsFacade).putString(mMockContext, Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE, newPackage); + verify(mSettingsFacade).putInt(mMockContext, + Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, + NetworkScoreManager.RECOMMENDATIONS_ENABLED_ON); } @Test @@ -341,6 +337,32 @@ public class NetworkScorerAppManagerTest { } @Test + public void testUpdateState_currentPackageNull_defaultNull() throws Exception { + setDefaultNetworkRecommendationPackage(null); + setNetworkRecoPackageSetting(null); + + mNetworkScorerAppManager.updateState(); + + verify(mSettingsFacade, never()).putString(any(), + eq(Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE), anyString()); + verify(mSettingsFacade, never()).putInt(any(), + eq(Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED), anyInt()); + } + + @Test + public void testUpdateState_currentPackageEmpty_defaultEmpty() throws Exception { + setDefaultNetworkRecommendationPackage(""); + setNetworkRecoPackageSetting(""); + + mNetworkScorerAppManager.updateState(); + + verify(mSettingsFacade, never()).putString(any(), + eq(Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE), anyString()); + verify(mSettingsFacade, never()).putInt(any(), + eq(Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED), anyInt()); + } + + @Test public void testUpdateState_currentPackageNotValid_sameAsDefault() throws Exception { String defaultPackage = "defaultPackage"; setDefaultNetworkRecommendationPackage(defaultPackage); diff --git a/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java new file mode 100644 index 000000000000..9a9c243023eb --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java @@ -0,0 +1,620 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.app.ActivityManager; +import android.app.AlarmManager; +import android.content.Context; +import android.content.ContextWrapper; +import android.os.UserHandle; +import android.provider.Settings; +import android.provider.Settings.Secure; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import android.test.mock.MockContentResolver; + +import com.android.internal.app.NightDisplayController; +import com.android.internal.app.NightDisplayController.LocalTime; +import com.android.internal.util.test.FakeSettingsProvider; +import com.android.server.display.DisplayTransformManager; +import com.android.server.display.NightDisplayService; +import com.android.server.twilight.TwilightManager; +import com.android.server.twilight.TwilightState; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; + +import java.util.Calendar; + +import static com.google.common.truth.Truth.assertWithMessage; +import static org.mockito.Mockito.doReturn; + +@RunWith(AndroidJUnit4.class) +public class NightDisplayServiceTest { + + private Context mContext; + private int mUserId; + + private TwilightManager mTwilightManager; + + private NightDisplayController mNightDisplayController; + private NightDisplayService mNightDisplayService; + + @Before + public void setUp() { + mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext())); + mUserId = ActivityManager.getCurrentUser(); + + doReturn(mContext).when(mContext).getApplicationContext(); + + final MockContentResolver cr = new MockContentResolver(mContext); + cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + doReturn(cr).when(mContext).getContentResolver(); + + final AlarmManager am = Mockito.mock(AlarmManager.class); + doReturn(am).when(mContext).getSystemService(Context.ALARM_SERVICE); + + final DisplayTransformManager dtm = Mockito.mock(DisplayTransformManager.class); + LocalServices.addService(DisplayTransformManager.class, dtm); + + mTwilightManager = Mockito.mock(TwilightManager.class); + LocalServices.addService(TwilightManager.class, mTwilightManager); + + mNightDisplayController = new NightDisplayController(mContext, mUserId); + mNightDisplayService = new NightDisplayService(mContext); + } + + @After + public void tearDown() { + LocalServices.removeServiceForTest(DisplayTransformManager.class); + LocalServices.removeServiceForTest(TwilightManager.class); + + mNightDisplayService = null; + mNightDisplayController = null; + + mTwilightManager = null; + + mUserId = UserHandle.USER_NULL; + mContext = null; + } + + @Test + public void customSchedule_whenStartedAfterNight_ifOffAfterNight_turnsOff() { + setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */); + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedAfterNight_ifOffBeforeNight_turnsOff() { + setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */); + setActivated(false /* activated */, -180 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedAfterNight_ifOffDuringNight_turnsOff() { + setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */); + setActivated(false /* activated */, -90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedAfterNight_ifOffInFuture_turnsOff() { + setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */); + setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedAfterNight_ifOnAfterNight_turnsOn() { + setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */); + setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void customSchedule_whenStartedAfterNight_ifOnBeforeNight_turnsOff() { + setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */); + setActivated(true /* activated */, -180 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedAfterNight_ifOnDuringNight_turnsOff() { + setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */); + setActivated(true /* activated */, -90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedAfterNight_ifOnInFuture_turnsOff() { + setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */); + setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedBeforeNight_ifOffAfterNight_turnsOff() { + setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */); + setActivated(false /* activated */, 180 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedBeforeNight_ifOffBeforeNight_turnsOff() { + setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */); + setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedBeforeNight_ifOffDuringNight_turnsOff() { + setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */); + setActivated(false /* activated */, 90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedBeforeNight_ifOffInPast_turnsOff() { + setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */); + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedBeforeNight_ifOnAfterNight_turnsOff() { + setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */); + setActivated(true /* activated */, 180 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedBeforeNight_ifOnBeforeNight_turnsOff() { + setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */); + setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedBeforeNight_ifOnDuringNight_turnsOff() { + setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */); + setActivated(true /* activated */, 90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedBeforeNight_ifOnInPast_turnsOn() { + setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */); + setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void customSchedule_whenStartedDuringNight_ifOffAfterNight_turnsOn() { + setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */); + setActivated(false /* activated */, 90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void customSchedule_whenStartedDuringNight_ifOffBeforeNight_turnsOn() { + setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */); + setActivated(false /* activated */, -90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void customSchedule_whenStartedDuringNight_ifOffDuringNightInFuture_turnsOn() { + setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */); + setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void customSchedule_whenStartedDuringNight_ifOffDuringNightInPast_turnsOff() { + setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */); + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedDuringNight_ifOnAfterNight_turnsOn() { + setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */); + setActivated(true /* activated */, 90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void customSchedule_whenStartedDuringNight_ifOnBeforeNight_turnsOn() { + setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */); + setActivated(true /* activated */, -90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void customSchedule_whenStartedDuringNight_ifOnDuringNightInFuture_turnsOn() { + setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */); + setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void customSchedule_whenStartedDuringNight_ifOnDuringNightInPast_turnsOn() { + setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */); + setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedAfterNight_ifOffAfterNight_turnsOff() { + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedAfterNight_ifOffBeforeNight_turnsOff() { + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(false /* activated */, -180 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedAfterNight_ifOffDuringNight_turnsOff() { + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(false /* activated */, -90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedAfterNight_ifOffInFuture_turnsOff() { + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedAfterNight_ifOnAfterNight_turnsOn() { + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedAfterNight_ifOnBeforeNight_turnsOff() { + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(true /* activated */, -180 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedAfterNight_ifOnDuringNight_turnsOff() { + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(true /* activated */, -90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedAfterNight_ifOnInFuture_turnsOff() { + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedBeforeNight_ifOffAfterNight_turnsOff() { + setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */); + setActivated(false /* activated */, 180 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedBeforeNight_ifOffBeforeNight_turnsOff() { + setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */); + setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedBeforeNight_ifOffDuringNight_turnsOff() { + setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */); + setActivated(false /* activated */, 90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedBeforeNight_ifOffInPast_turnsOff() { + setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */); + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedBeforeNight_ifOnAfterNight_turnsOff() { + setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */); + setActivated(true /* activated */, 180 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedBeforeNight_ifOnBeforeNight_turnsOff() { + setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */); + setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedBeforeNight_ifOnDuringNight_turnsOff() { + setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */); + setActivated(true /* activated */, 90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedBeforeNight_ifOnInPast_turnsOn() { + setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */); + setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedDuringNight_ifOffAfterNight_turnsOn() { + setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */); + setActivated(false /* activated */, 90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedDuringNight_ifOffBeforeNight_turnsOn() { + setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */); + setActivated(false /* activated */, -90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedDuringNight_ifOffDuringNightInFuture_turnsOn() { + setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */); + setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedDuringNight_ifOffDuringNightInPast_turnsOff() { + setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */); + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedDuringNight_ifOnAfterNight_turnsOn() { + setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */); + setActivated(true /* activated */, 90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedDuringNight_ifOnBeforeNight_turnsOn() { + setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */); + setActivated(true /* activated */, -90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedDuringNight_ifOnDuringNightInFuture_turnsOn() { + setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */); + setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedDuringNight_ifOnDuringNightInPast_turnsOn() { + setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */); + setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + /** + * Convenience for making a {@link LocalTime} instance with an offset relative to now. + * + * @param offsetMinutes the offset relative to now (in minutes) + * @return the LocalTime instance + */ + private LocalTime getLocalTimeRelativeToNow(int offsetMinutes) { + final Calendar c = Calendar.getInstance(); + c.add(Calendar.MINUTE, offsetMinutes); + return new LocalTime(c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE)); + } + + /** + * Configures Night display to use a custom schedule. + * + * @param startTimeOffset the offset relative to now to activate Night display (in minutes) + * @param endTimeOffset the offset relative to now to deactivate Night display (in minutes) + */ + private void setAutoModeCustom(int startTimeOffset, int endTimeOffset) { + mNightDisplayController.setAutoMode(NightDisplayController.AUTO_MODE_CUSTOM); + mNightDisplayController.setCustomStartTime(getLocalTimeRelativeToNow(startTimeOffset)); + mNightDisplayController.setCustomEndTime(getLocalTimeRelativeToNow(endTimeOffset)); + } + + /** + * Configures Night display to use the twilight schedule. + * + * @param sunsetOffset the offset relative to now for sunset (in minutes) + * @param sunriseOffset the offset relative to now for sunrise (in minutes) + */ + private void setAutoModeTwilight(int sunsetOffset, int sunriseOffset) { + mNightDisplayController.setAutoMode(NightDisplayController.AUTO_MODE_TWILIGHT); + + final LocalTime sunset = getLocalTimeRelativeToNow(sunsetOffset); + final LocalTime sunrise = getLocalTimeRelativeToNow(sunriseOffset); + + final Calendar now = Calendar.getInstance(); + long sunsetMillis = sunset.getDateTimeBefore(now).getTimeInMillis(); + long sunriseMillis = sunrise.getDateTimeBefore(now).getTimeInMillis(); + if (sunsetMillis < sunriseMillis) { + sunsetMillis = sunset.getDateTimeAfter(now).getTimeInMillis(); + } else { + sunriseMillis = sunrise.getDateTimeAfter(now).getTimeInMillis(); + } + + final TwilightState state = new TwilightState(sunriseMillis, sunsetMillis); + doReturn(state).when(mTwilightManager).getLastTwilightState(); + } + + /** + * Configures the Night display activated state. + * + * @param activated {@code true} if Night display should be activated + * @param lastActivatedTimeOffset the offset relative to now to record that Night display was + activated (in minutes) + */ + private void setActivated(boolean activated, int lastActivatedTimeOffset) { + mNightDisplayController.setActivated(activated); + + final Calendar c = Calendar.getInstance(); + c.add(Calendar.MINUTE, lastActivatedTimeOffset); + Secure.putLongForUser(mContext.getContentResolver(), + Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, c.getTimeInMillis(), mUserId); + } + + /** + * Convenience method to start {@link #mNightDisplayService}. + */ + private void startService() { + Secure.putIntForUser(mContext.getContentResolver(), Secure.USER_SETUP_COMPLETE, 1, mUserId); + + InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + mNightDisplayService.onStart(); + mNightDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + mNightDisplayService.onStartUser(mUserId); + } + }); + } + + /** + * Convenience method for asserting whether Night display should be activated. + * + * @param activated the expected activated state of Night display + */ + private void assertActivated(boolean activated) { + assertWithMessage("Invalid Night display activated state") + .that(mNightDisplayController.isActivated()) + .isEqualTo(activated); + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java index a23a6b22d9e8..649de4a783fa 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java @@ -20,6 +20,7 @@ import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -33,8 +34,7 @@ import org.junit.runner.RunWith; * runtest frameworks-services -c com.android.server.wm.TaskSnapshotCacheTest */ @SmallTest -// TODO(b/35196891): Add back to presubmit once the bug is fixed. -//@Presubmit +@Presubmit @RunWith(AndroidJUnit4.class) public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase { diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java index 2b4d9fb4800a..f253632a1765 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static com.android.server.wm.AppTransition.TRANSIT_UNSET; import static com.android.server.wm.TaskSnapshotController.*; import static junit.framework.Assert.assertEquals; @@ -76,12 +77,19 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { public void testGetSnapshotMode() throws Exception { final WindowState disabledWindow = createWindow(null, FIRST_APPLICATION_WINDOW, mDisplayContent, "disabledWindow"); - disabledWindow.mAppToken.setDisablePreviewSnapshots(true); + disabledWindow.mAppToken.setDisablePreviewScreenshots(true); assertEquals(SNAPSHOT_MODE_APP_THEME, sWm.mTaskSnapshotController.getSnapshotMode(disabledWindow.getTask())); + final WindowState normalWindow = createWindow(null, FIRST_APPLICATION_WINDOW, mDisplayContent, "normalWindow"); assertEquals(SNAPSHOT_MODE_REAL, sWm.mTaskSnapshotController.getSnapshotMode(normalWindow.getTask())); + + final WindowState secureWindow = createWindow(null, + FIRST_APPLICATION_WINDOW, mDisplayContent, "secureWindow"); + secureWindow.mAttrs.flags |= FLAG_SECURE; + assertEquals(SNAPSHOT_MODE_APP_THEME, + sWm.mTaskSnapshotController.getSnapshotMode(secureWindow.getTask())); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java index 18d0c321d181..8146763ae871 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java @@ -68,7 +68,11 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase { } private void cleanDirectory() { - for (File file : new File(sFilesDir, "snapshots").listFiles()) { + final File[] files = new File(sFilesDir, "snapshots").listFiles(); + if (files == null) { + return; + } + for (File file : files) { if (!file.isDirectory()) { file.delete(); } diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java index 717ddf26eb2f..e2868d7056b0 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -171,11 +171,25 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - mSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 50, 100), 10); + mSurface.mSystemBarBackgroundPainter.drawStatusBarBackground( + mockCanvas, new Rect(0, 0, 50, 100), 10); verify(mockCanvas).drawRect(eq(50.0f), eq(0.0f), eq(90.0f), eq(10.0f), any()); } @Test + public void testDrawStatusBarBackground_nullFrame() { + setupSurface(100, 100); + final Rect insets = new Rect(0, 10, 10, 0); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.mSystemBarBackgroundPainter.drawStatusBarBackground( + mockCanvas, null, 10); + verify(mockCanvas).drawRect(eq(0.0f), eq(0.0f), eq(90.0f), eq(10.0f), any()); + } + + @Test public void testDrawStatusBarBackground_nope() { setupSurface(100, 100); final Rect insets = new Rect(0, 10, 10, 0); @@ -183,7 +197,8 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - mSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 100, 100), 10); + mSurface.mSystemBarBackgroundPainter.drawStatusBarBackground( + mockCanvas, new Rect(0, 0, 100, 100), 10); verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); } @@ -196,7 +211,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - mSurface.drawNavigationBarBackground(mockCanvas); + mSurface.mSystemBarBackgroundPainter.drawNavigationBarBackground(mockCanvas); verify(mockCanvas).drawRect(eq(new Rect(0, 90, 100, 100)), any()); } @@ -209,7 +224,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - mSurface.drawNavigationBarBackground(mockCanvas); + mSurface.mSystemBarBackgroundPainter.drawNavigationBarBackground(mockCanvas); verify(mockCanvas).drawRect(eq(new Rect(0, 0, 10, 100)), any()); } @@ -222,7 +237,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - mSurface.drawNavigationBarBackground(mockCanvas); + mSurface.mSystemBarBackgroundPainter.drawNavigationBarBackground(mockCanvas); verify(mockCanvas).drawRect(eq(new Rect(90, 0, 100, 100)), any()); } } diff --git a/telephony/java/android/telephony/ims/ImsServiceProxy.java b/telephony/java/android/telephony/ims/ImsServiceProxy.java index 38ea6e6ff9a0..a75cd86dcf07 100644 --- a/telephony/java/android/telephony/ims/ImsServiceProxy.java +++ b/telephony/java/android/telephony/ims/ImsServiceProxy.java @@ -120,7 +120,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) throws RemoteException { synchronized (mLock) { - checkBinderConnection(); + checkServiceIsReady(); return getServiceInterface(mBinder).startSession(mSlotId, mSupportedFeature, incomingCallIntent, listener); } @@ -129,7 +129,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur @Override public void endSession(int sessionId) throws RemoteException { synchronized (mLock) { - checkBinderConnection(); + checkServiceIsReady(); getServiceInterface(mBinder).endSession(mSlotId, mSupportedFeature, sessionId); } } @@ -138,7 +138,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur public boolean isConnected(int callServiceType, int callType) throws RemoteException { synchronized (mLock) { - checkBinderConnection(); + checkServiceIsReady(); return getServiceInterface(mBinder).isConnected(mSlotId, mSupportedFeature, callServiceType, callType); } @@ -147,7 +147,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur @Override public boolean isOpened() throws RemoteException { synchronized (mLock) { - checkBinderConnection(); + checkServiceIsReady(); return getServiceInterface(mBinder).isOpened(mSlotId, mSupportedFeature); } } @@ -156,7 +156,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur public void addRegistrationListener(IImsRegistrationListener listener) throws RemoteException { synchronized (mLock) { - checkBinderConnection(); + checkServiceIsReady(); getServiceInterface(mBinder).addRegistrationListener(mSlotId, mSupportedFeature, listener); } @@ -166,7 +166,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur public void removeRegistrationListener(IImsRegistrationListener listener) throws RemoteException { synchronized (mLock) { - checkBinderConnection(); + checkServiceIsReady(); getServiceInterface(mBinder).removeRegistrationListener(mSlotId, mSupportedFeature, listener); } @@ -176,7 +176,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType) throws RemoteException { synchronized (mLock) { - checkBinderConnection(); + checkServiceIsReady(); return getServiceInterface(mBinder).createCallProfile(mSlotId, mSupportedFeature, sessionId, callServiceType, callType); } @@ -186,7 +186,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile, IImsCallSessionListener listener) throws RemoteException { synchronized (mLock) { - checkBinderConnection(); + checkServiceIsReady(); return getServiceInterface(mBinder).createCallSession(mSlotId, mSupportedFeature, sessionId, profile, listener); } @@ -196,7 +196,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur public IImsCallSession getPendingCallSession(int sessionId, String callId) throws RemoteException { synchronized (mLock) { - checkBinderConnection(); + checkServiceIsReady(); return getServiceInterface(mBinder).getPendingCallSession(mSlotId, mSupportedFeature, sessionId, callId); } @@ -205,7 +205,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur @Override public IImsUt getUtInterface() throws RemoteException { synchronized (mLock) { - checkBinderConnection(); + checkServiceIsReady(); return getServiceInterface(mBinder).getUtInterface(mSlotId, mSupportedFeature); } } @@ -213,7 +213,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur @Override public IImsConfig getConfigInterface() throws RemoteException { synchronized (mLock) { - checkBinderConnection(); + checkServiceIsReady(); return getServiceInterface(mBinder).getConfigInterface(mSlotId, mSupportedFeature); } } @@ -221,7 +221,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur @Override public void turnOnIms() throws RemoteException { synchronized (mLock) { - checkBinderConnection(); + checkServiceIsReady(); getServiceInterface(mBinder).turnOnIms(mSlotId, mSupportedFeature); } } @@ -229,7 +229,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur @Override public void turnOffIms() throws RemoteException { synchronized (mLock) { - checkBinderConnection(); + checkServiceIsReady(); getServiceInterface(mBinder).turnOffIms(mSlotId, mSupportedFeature); } } @@ -237,7 +237,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur @Override public IImsEcbm getEcbmInterface() throws RemoteException { synchronized (mLock) { - checkBinderConnection(); + checkServiceIsReady(); return getServiceInterface(mBinder).getEcbmInterface(mSlotId, mSupportedFeature); } } @@ -246,7 +246,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur public void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException { synchronized (mLock) { - checkBinderConnection(); + checkServiceIsReady(); getServiceInterface(mBinder).setUiTTYMode(mSlotId, mSupportedFeature, uiTtyMode, onComplete); } @@ -255,7 +255,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur @Override public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException { synchronized (mLock) { - checkBinderConnection(); + checkServiceIsReady(); return getServiceInterface(mBinder).getMultiEndpointInterface(mSlotId, mSupportedFeature); } @@ -264,7 +264,8 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur @Override public int getFeatureStatus() { synchronized (mLock) { - if (mFeatureStatusCached != null) { + if (isBinderAlive() && mFeatureStatusCached != null) { + Log.i(LOG_TAG, "getFeatureStatus - returning cached: " + mFeatureStatusCached); return mFeatureStatusCached; } } @@ -277,6 +278,7 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur // Cache only non-null value for feature status. mFeatureStatusCached = status; } + Log.i(LOG_TAG, "getFeatureStatus - returning " + status); return status; } @@ -301,10 +303,28 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur mStatusCallback = c; } + /** + * @return Returns true if the ImsService is ready to take commands, false otherwise. If this + * method returns false, it doesn't mean that the Binder connection is not available (use + * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands + * at this time. + * + * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take + * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_NOT_AVAILABLE}. + */ + public boolean isBinderReady() { + return isBinderAlive() && getFeatureStatus() == ImsFeature.STATE_READY; + } + @Override public boolean isBinderAlive() { - return mIsAvailable && getFeatureStatus() == ImsFeature.STATE_READY && mBinder != null && - mBinder.isBinderAlive(); + return mIsAvailable && mBinder != null && mBinder.isBinderAlive(); + } + + protected void checkServiceIsReady() throws RemoteException { + if (!isBinderReady()) { + throw new RemoteException("ImsServiceProxy is not ready to accept commands."); + } } private IImsServiceController getServiceInterface(IBinder b) { diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java index 988dd588ecad..395f1ccc7a29 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -150,7 +150,7 @@ public abstract class ImsFeature { private void notifyFeatureState(@ImsState int state) { if (mStatusCallback != null) { try { - Log.i(LOG_TAG, "notifying ImsFeatureState"); + Log.i(LOG_TAG, "notifying ImsFeatureState=" + state); mStatusCallback.notifyImsFeatureStatus(state); } catch (RemoteException e) { mStatusCallback = null; diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java index ff632a5cdc7c..b133a44d0117 100644 --- a/wifi/java/android/net/wifi/RttManager.java +++ b/wifi/java/android/net/wifi/RttManager.java @@ -660,10 +660,10 @@ public class RttManager { @Deprecated public int tx_rate; - /** average transmit rate. Unit (100kbps). */ + /** average transmit rate. Unit (kbps). */ public int txRate; - /** average receiving rate Unit (100kbps). */ + /** average receiving rate Unit (kbps). */ public int rxRate; /** @@ -673,7 +673,7 @@ public class RttManager { @Deprecated public long rtt_ns; - /** average round trip time in 0.1 nano second. */ + /** average round trip time in picoseconds. */ public long rtt; /** @@ -683,7 +683,7 @@ public class RttManager { @Deprecated public long rtt_sd_ns; - /** standard deviation of RTT in 0.1 ns. */ + /** standard deviation of RTT in picoseconds. */ public long rttStandardDeviation; /** @@ -693,7 +693,7 @@ public class RttManager { @Deprecated public long rtt_spread_ns; - /** spread (i.e. max - min) RTT in 0.1 ns. */ + /** spread (i.e. max - min) RTT in picoseconds. */ public long rttSpread; /** diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index e31a74b42b14..f7333e281982 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -839,6 +839,10 @@ public class WifiConfiguration implements Parcelable { */ public static final int NETWORK_SELECTION_ENABLE = 0; /** + * The starting index for network selection disabled reasons + */ + public static final int NETWORK_SELECTION_DISABLED_STARTING_INDEX = 1; + /** * @deprecated it is not used any more. * This network is disabled because higher layer (>2) network is bad */ |