diff options
407 files changed, 10612 insertions, 5153 deletions
diff --git a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java index c1491956c798..bd3b6737f505 100644 --- a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java +++ b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java @@ -52,7 +52,7 @@ public class TextClassificationManagerPerfTest { BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { textClassificationManager.getTextClassifier(); - textClassificationManager.invalidate(); + textClassificationManager.invalidateForTesting(); } } @@ -68,7 +68,7 @@ public class TextClassificationManagerPerfTest { BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { textClassificationManager.getTextClassifier(); - textClassificationManager.invalidate(); + textClassificationManager.invalidateForTesting(); } } } diff --git a/api/test-current.txt b/api/test-current.txt index aacf2c1fa9a6..95f4a2e480b8 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -3262,6 +3262,7 @@ package android.view.autofill { ctor public AutofillId(@NonNull android.view.autofill.AutofillId, int); ctor public AutofillId(int, int); ctor public AutofillId(@NonNull android.view.autofill.AutofillId, long, int); + method public boolean equalsIgnoreSession(@Nullable android.view.autofill.AutofillId); } public final class AutofillManager { @@ -3390,6 +3391,7 @@ package android.view.contentcapture { package android.view.inputmethod { public final class InputMethodManager { + method public int getDisplayId(); method public boolean isInputMethodPickerShown(); } diff --git a/cmds/am/proto/instrumentation_data.proto b/cmds/am/proto/instrumentation_data.proto index 8e29f9645568..23e951992305 100644 --- a/cmds/am/proto/instrumentation_data.proto +++ b/cmds/am/proto/instrumentation_data.proto @@ -38,6 +38,7 @@ message ResultsBundle { message TestStatus { optional sint32 result_code = 3; optional ResultsBundle results = 4; + optional string logcat = 5; } enum SessionStatusCode { diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java index 70baa8702ba9..4d7b5a79b4f7 100644 --- a/cmds/am/src/com/android/commands/am/Instrument.java +++ b/cmds/am/src/com/android/commands/am/Instrument.java @@ -38,6 +38,7 @@ import android.view.IWindowManager; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -62,8 +63,15 @@ import java.util.Locale; * other: Failure */ public class Instrument { + private static final String TAG = "am"; + public static final String DEFAULT_LOG_DIR = "instrument-logs"; + private static final int STATUS_TEST_PASSED = 0; + private static final int STATUS_TEST_STARTED = 1; + private static final int STATUS_TEST_FAILED_ASSERTION = -1; + private static final int STATUS_TEST_FAILED_OTHER = -2; + private final IActivityManager mAm; private final IPackageManager mPm; private final IWindowManager mWm; @@ -207,6 +215,8 @@ public class Instrument { private File mLog; + private long mTestStartMs; + ProtoStatusReporter() { if (protoFile) { if (logPath == null) { @@ -241,10 +251,22 @@ public class Instrument { Bundle results) { final ProtoOutputStream proto = new ProtoOutputStream(); - final long token = proto.start(InstrumentationData.Session.TEST_STATUS); + final long testStatusToken = proto.start(InstrumentationData.Session.TEST_STATUS); + proto.write(InstrumentationData.TestStatus.RESULT_CODE, resultCode); writeBundle(proto, InstrumentationData.TestStatus.RESULTS, results); - proto.end(token); + + if (resultCode == STATUS_TEST_STARTED) { + // Logcat -T takes wall clock time (!?) + mTestStartMs = System.currentTimeMillis(); + } else { + if (mTestStartMs > 0) { + proto.write(InstrumentationData.TestStatus.LOGCAT, readLogcat(mTestStartMs)); + } + mTestStartMs = 0; + } + + proto.end(testStatusToken); outputProto(proto); } @@ -254,12 +276,12 @@ public class Instrument { Bundle results) { final ProtoOutputStream proto = new ProtoOutputStream(); - final long token = proto.start(InstrumentationData.Session.SESSION_STATUS); + final long sessionStatusToken = proto.start(InstrumentationData.Session.SESSION_STATUS); proto.write(InstrumentationData.SessionStatus.STATUS_CODE, InstrumentationData.SESSION_FINISHED); proto.write(InstrumentationData.SessionStatus.RESULT_CODE, resultCode); writeBundle(proto, InstrumentationData.SessionStatus.RESULTS, results); - proto.end(token); + proto.end(sessionStatusToken); outputProto(proto); } @@ -268,11 +290,11 @@ public class Instrument { public void onError(String errorText, boolean commandError) { final ProtoOutputStream proto = new ProtoOutputStream(); - final long token = proto.start(InstrumentationData.Session.SESSION_STATUS); + final long sessionStatusToken = proto.start(InstrumentationData.Session.SESSION_STATUS); proto.write(InstrumentationData.SessionStatus.STATUS_CODE, InstrumentationData.SESSION_ABORTED); proto.write(InstrumentationData.SessionStatus.ERROR_TEXT, errorText); - proto.end(token); + proto.end(sessionStatusToken); outputProto(proto); } @@ -514,5 +536,43 @@ public class Instrument { } } } + + private static String readLogcat(long startTimeMs) { + try { + // Figure out the timestamp arg for logcat. + final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + final String timestamp = format.format(new Date(startTimeMs)); + + // Start the process + final Process process = new ProcessBuilder() + .command("logcat", "-d", "-v threadtime,uid", "-T", timestamp) + .start(); + + // Nothing to write. Don't let the command accidentally block. + process.getOutputStream().close(); + + // Read the output + final StringBuilder str = new StringBuilder(); + final InputStreamReader reader = new InputStreamReader(process.getInputStream()); + char[] buffer = new char[4096]; + int amt; + while ((amt = reader.read(buffer, 0, buffer.length)) >= 0) { + if (amt > 0) { + str.append(buffer, 0, amt); + } + } + + try { + process.waitFor(); + } catch (InterruptedException ex) { + // We already have the text, drop the exception. + } + + return str.toString(); + + } catch (IOException ex) { + return "Error reading logcat command:\n" + ex.toString(); + } + } } diff --git a/cmds/incidentd/OWNERS b/cmds/incidentd/OWNERS index cede4eae50ad..bcdcfc3cafed 100644 --- a/cmds/incidentd/OWNERS +++ b/cmds/incidentd/OWNERS @@ -1,3 +1,4 @@ joeo@google.com -kwekua@google.com +yaochen@google.com yanmin@google.com +zhouwenjie@google.com diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS index 13157505fc28..380e499a5abf 100644 --- a/cmds/statsd/OWNERS +++ b/cmds/statsd/OWNERS @@ -1,11 +1,7 @@ -bookatz@google.com -cjyu@google.com -dwchen@google.com -jinyithu@google.com +jianjin@google.com joeo@google.com -kwekua@google.com +jtnguyen@google.com +muhammadq@google.com singhtejinder@google.com -stlafon@google.com yaochen@google.com -yanglu@google.com yro@google.com diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 318d90ca9199..3a84b79fc681 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -48,6 +48,7 @@ import "frameworks/base/core/proto/android/stats/docsui/docsui_enums.proto"; import "frameworks/base/core/proto/android/stats/enums.proto"; import "frameworks/base/core/proto/android/stats/intelligence/enums.proto"; import "frameworks/base/core/proto/android/stats/launcher/launcher.proto"; +import "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.proto"; import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto"; import "frameworks/base/core/proto/android/stats/style/style_enums.proto"; import "frameworks/base/core/proto/android/telecomm/enums.proto"; @@ -283,6 +284,16 @@ message Atom { ThermalThrottlingSeverityStateChanged thermal_throttling_severity_state_changed = 189; RoleRequestResultReported role_request_result_reported = 190 [(log_from_module) = "permissioncontroller"]; + MediametricsAudiopolicyReported mediametrics_audiopolicy_reported = 191; + MediametricsAudiorecordReported mediametrics_audiorecord_reported = 192; + MediametricsAudiothreadReported mediametrics_audiothread_reported = 193; + MediametricsAudiotrackReported mediametrics_audiotrack_reported = 194; + MediametricsCodecReported mediametrics_codec_reported = 195; + MediametricsDrmWidevineReported mediametrics_drm_widevine_reported = 196; + MediametricsExtractorReported mediametrics_extractor_reported = 197; + MediametricsMediadrmReported mediametrics_mediadrm_reported = 198; + MediametricsNuPlayerReported mediametrics_nuplayer_reported = 199; + MediametricsRecorderReported mediametrics_recorder_reported = 200; } // Pulled events will start at field 10000. @@ -5815,6 +5826,160 @@ message ProcessStartTime { } /** + * Track Media Codec usage + * Logged from: + * frameworks/av/media/libstagefright/MediaCodec.cpp + * frameworks/av/services/mediaanalytics/statsd_codec.cpp + */ +message MediametricsCodecReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional android.stats.mediametrics.CodecData codec_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track Media Extractor (pulling video/audio streams out of containers) usage + * Logged from: + * frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp + * frameworks/av/services/mediaanalytics/statsd_extractor.cpp + */ +message MediametricsExtractorReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional android.stats.mediametrics.ExtractorData extractor_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track how we arbitrate between microphone/input requests. + * Logged from + * frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp + * frameworks/av/services/mediaanalytics/statsd_audiopolicy.cpp + */ +message MediametricsAudiopolicyReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional android.stats.mediametrics.AudioPolicyData audiopolicy_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track how we arbitrate between microphone requests. + * Logged from + * frameworks/av/media/libaudioclient/AudioRecord.cpp + * frameworks/av/services/mediaanalytics/statsd_audiorecord.cpp + */ +message MediametricsAudiorecordReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional android.stats.mediametrics.AudioRecordData audiorecord_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track how we arbitrate between microphone/input requests. + * Logged from + * frameworks/av/media/libnblog/ReportPerformance.cpp + * frameworks/av/services/mediaanalytics/statsd_audiothread.cpp + */ +message MediametricsAudiothreadReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional android.stats.mediametrics.AudioThreadData audiothread_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track how we arbitrate between microphone/input requests. + * Logged from + * frameworks/av/media/libaudioclient/AudioTrack.cpp + * frameworks/av/services/mediaanalytics/statsd_audiotrack.cpp + */ +message MediametricsAudiotrackReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional android.stats.mediametrics.AudioTrackData audiotrack_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track information about DRM framework performance + * Logged from + * frameworks/av/drm/libmediadrm/DrmHal.cpp + * frameworks/av/services/mediaanalytics/statsd_drm.cpp + */ +message MediametricsMediadrmReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + // vendor+description tell about which DRM plugin is in use on this device + optional string vendor = 5; + optional string description = 6; + // from frameworks/av/drm/libmediadrm/protos/metrics.proto + optional bytes framework_stats = 7 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track information about the widevine DRM plugin performance + * Logged from + * vendor/widevine/libwvdrmengine/cdm/metrics + * frameworks/av/services/mediaanalytics/statsd_drm.cpp + */ +message MediametricsDrmWidevineReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional bytes vendor_specific_stats = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track information about recordings (e.g. camcorder) + * Logged from + * frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp + * frameworks/av/services/mediaanalytics/statsd_recorder.cpp + */ +message MediametricsRecorderReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional android.stats.mediametrics.RecorderData recorder_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track Media Player usage + * Logged from: + * frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp + * frameworks/av/services/mediaanalytics/statsd_nuplayer.cpp + */ +message MediametricsNuPlayerReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional android.stats.mediametrics.NuPlayerData nuplayer_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** * State of a dangerous permission requested by a package */ message DangerousPermissionState { diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java index fc7b778b7a86..3e705fdf8a12 100644 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java +++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java @@ -57,6 +57,7 @@ public class TestDrive { "AID_BLUETOOTH", "AID_LMKD", "com.android.managedprovisioning", + "AID_MEDIA", "AID_NETWORK_STACK" }; private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName()); diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java index 1b7fbfe0e32c..052445095afe 100644 --- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java +++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java @@ -95,7 +95,8 @@ public final class Telecom extends BaseCommand { + "telecom set-phone-account-disabled: Disables the given phone account, if it \n" + " has already been registered with telecom.\n" + "\n" - + "telecom set-default-dialer: Sets the default dialer to the given component. \n" + + "telecom set-default-dialer: Sets the override default dialer to the given " + + "component; this will override whatever the dialer role is set to. \n" + "\n" + "telecom get-default-dialer: Displays the current default dialer. \n" + "\n" @@ -254,13 +255,8 @@ public final class Telecom extends BaseCommand { private void runSetDefaultDialer() throws RemoteException { final String packageName = nextArgRequired(); - final boolean success = mTelecomService.setDefaultDialer(packageName); - if (success) { - System.out.println("Success - " + packageName + " set as default dialer."); - } else { - System.out.println("Error - " + packageName + " is not an installed Dialer app, \n" - + " or is already the default dialer."); - } + mTelecomService.setTestDefaultDialer(packageName); + System.out.println("Success - " + packageName + " set as override default dialer."); } private void runGetDefaultDialer() throws RemoteException { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 43531eddf638..2914f6c5abf2 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -126,7 +126,6 @@ import android.view.autofill.AutofillManager; import android.view.autofill.AutofillManager.AutofillClient; import android.view.autofill.AutofillPopupWindow; import android.view.autofill.IAutofillWindowPresenter; -import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; import android.widget.AdapterView; @@ -840,7 +839,7 @@ public class Activity extends ContextThemeWrapper /** The autofill manager. Always access via {@link #getAutofillManager()}. */ @Nullable private AutofillManager mAutofillManager; - /** The content capture manager. Always access via {@link #getContentCaptureManager()}. */ + /** The content capture manager. Access via {@link #getContentCaptureManager()}. */ @Nullable private ContentCaptureManager mContentCaptureManager; private final ArrayList<Application.ActivityLifecycleCallbacks> mActivityLifecycleCallbacks = @@ -1092,12 +1091,11 @@ public class Activity extends ContextThemeWrapper case CONTENT_CAPTURE_START: //TODO(b/111276913): decide whether the InteractionSessionId should be // saved / restored in the activity bundle - probably not - int flags = 0; - if ((getWindow().getAttributes().flags - & WindowManager.LayoutParams.FLAG_SECURE) != 0) { - flags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE; + final Window window = getWindow(); + if (window != null) { + cm.updateWindowAttributes(window.getAttributes()); } - cm.onActivityCreated(mToken, getComponentName(), flags); + cm.onActivityCreated(mToken, getComponentName()); break; case CONTENT_CAPTURE_RESUME: cm.onActivityResumed(); @@ -2349,7 +2347,7 @@ public class Activity extends ContextThemeWrapper * * @param cancellationSignal A signal to cancel the operation in progress. * @param callback The callback to send the action list. The actions list cannot - * contain <code>null</code> elements. + * contain <code>null</code> elements. You can call this on any thread. */ public void onGetDirectActions(@NonNull CancellationSignal cancellationSignal, @NonNull Consumer<List<DirectAction>> callback) { @@ -2360,10 +2358,13 @@ public class Activity extends ContextThemeWrapper * This is called to perform an action previously defined by the app. * Apps also have access to {@link #getVoiceInteractor()} to follow up on the action. * - * @param actionId The ID for the action - * @param arguments Any additional arguments provided by the caller + * @param actionId The ID for the action you previously reported via + * {@link #onGetDirectActions(CancellationSignal, Consumer)}. + * @param arguments Any additional arguments provided by the caller that are + * specific to the given action. * @param cancellationSignal A signal to cancel the operation in progress. - * @param resultListener The callback to provide the result back to the caller + * @param resultListener The callback to provide the result back to the caller. + * You can call this on any thread. The result bundle is action specific. * * @see #onGetDirectActions(CancellationSignal, Consumer) */ @@ -3782,6 +3783,9 @@ public class Activity extends ContextThemeWrapper View decor = mDecor; if (decor != null && decor.getParent() != null) { getWindowManager().updateViewLayout(decor, params); + if (mContentCaptureManager != null) { + mContentCaptureManager.updateWindowAttributes(params); + } } } } diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index fc6fffabc917..b64b2dcc4f61 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -317,7 +317,7 @@ public class ActivityView extends ViewGroup { * regions and avoid focus switches by touches on this view. */ public void onLocationChanged() { - updateTapExcludeRegion(); + updateLocationAndTapExcludeRegion(); } @Override @@ -329,39 +329,52 @@ public class ActivityView extends ViewGroup { public boolean gatherTransparentRegion(Region region) { // The tap exclude region may be affected by any view on top of it, so we detect the // possible change by monitoring this function. - updateTapExcludeRegion(); + updateLocationAndTapExcludeRegion(); return super.gatherTransparentRegion(region); } - /** Compute and send current tap exclude region to WM for this view. */ - private void updateTapExcludeRegion() { - if (!isAttachedToWindow()) { - return; - } - if (!canReceivePointerEvents()) { - cleanTapExcludeRegion(); + /** + * Sends current location in window and tap exclude region to WM for this view. + */ + private void updateLocationAndTapExcludeRegion() { + if (mVirtualDisplay == null || !isAttachedToWindow()) { return; } try { + int x = mLocationInWindow[0]; + int y = mLocationInWindow[1]; getLocationInWindow(mLocationInWindow); - final int x = mLocationInWindow[0]; - final int y = mLocationInWindow[1]; - mTapExcludeRegion.set(x, y, x + getWidth(), y + getHeight()); - - // There might be views on top of us. We need to subtract those areas from the tap - // exclude region. - final ViewParent parent = getParent(); - if (parent instanceof ViewGroup) { - ((ViewGroup) parent).subtractObscuredTouchableRegion(mTapExcludeRegion, this); + if (x != mLocationInWindow[0] || y != mLocationInWindow[1]) { + x = mLocationInWindow[0]; + y = mLocationInWindow[1]; + WindowManagerGlobal.getWindowSession().updateDisplayContentLocation( + getWindow(), x, y, mVirtualDisplay.getDisplay().getDisplayId()); } - - WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(), - mTapExcludeRegion); + updateTapExcludeRegion(x, y); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } } + /** Computes and sends current tap exclude region to WM for this view. */ + private void updateTapExcludeRegion(int x, int y) throws RemoteException { + if (!canReceivePointerEvents()) { + cleanTapExcludeRegion(); + return; + } + mTapExcludeRegion.set(x, y, x + getWidth(), y + getHeight()); + + // There might be views on top of us. We need to subtract those areas from the tap + // exclude region. + final ViewParent parent = getParent(); + if (parent != null) { + parent.subtractObscuredTouchableRegion(mTapExcludeRegion, this); + } + + WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(), + mTapExcludeRegion); + } + private class SurfaceCallback implements SurfaceHolder.Callback { @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { @@ -379,7 +392,7 @@ public class ActivityView extends ViewGroup { mVirtualDisplay.setDisplayState(true); } - updateTapExcludeRegion(); + updateLocationAndTapExcludeRegion(); } @Override @@ -387,7 +400,7 @@ public class ActivityView extends ViewGroup { if (mVirtualDisplay != null) { mVirtualDisplay.resize(width, height, getBaseDisplayDensity()); } - updateTapExcludeRegion(); + updateLocationAndTapExcludeRegion(); } @Override @@ -471,7 +484,8 @@ public class ActivityView extends ViewGroup { try { // TODO: Find a way to consolidate these calls to the server. - wm.reparentDisplayContent(displayId, mRootSurfaceControl); + WindowManagerGlobal.getWindowSession().reparentDisplayContent( + getWindow(), mRootSurfaceControl, displayId); wm.dontOverrideDisplayInfo(displayId); if (mSingleTaskInstance) { mActivityTaskManager.setDisplayToSingleTaskInstance(displayId); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 82a34ceea8b9..1ad0cfd6e13f 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -2764,6 +2764,9 @@ public class ApplicationPackageManager extends PackageManager { if (itemInfo.packageName != null) { dr = getDrawable(itemInfo.packageName, itemInfo.icon, appInfo); } + if (dr == null && itemInfo != appInfo) { + dr = loadUnbadgedItemIcon(appInfo, appInfo); + } if (dr == null) { dr = itemInfo.loadDefaultIcon(this); } diff --git a/core/java/android/app/DirectAction.java b/core/java/android/app/DirectAction.java index d191f4b67364..ef3627b26a5d 100644 --- a/core/java/android/app/DirectAction.java +++ b/core/java/android/app/DirectAction.java @@ -22,12 +22,19 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; +import android.view.accessibility.AccessibilityNodeInfo; import com.android.internal.util.Preconditions; +import java.util.Objects; + /** * Represents a abstract action that can be perform on this app. This are requested from - * outside the app's UI (eg by SystemUI or assistant). + * outside the app's UI (eg by SystemUI or assistant). The semantics of these actions are + * not specified by the OS. This allows open-ended and scalable approach for defining how + * an app interacts with components that expose alternative interaction models to the user + * such as the assistant, SystemUI, etc. You can use {@link #equals(Object)} to compare + * instances of this class. */ public final class DirectAction implements Parcelable { @@ -91,7 +98,7 @@ public final class DirectAction implements Parcelable { } /** - * Returns the ID for this action. + * @return the ID for this action. */ @NonNull public String getId() { @@ -99,7 +106,7 @@ public final class DirectAction implements Parcelable { } /** - * Returns any extras associated with this action. + * @return any extras associated with this action. */ @Nullable public Bundle getExtras() { @@ -107,7 +114,7 @@ public final class DirectAction implements Parcelable { } /** - * Returns the LocusId for the current state for the app + * @return the LocusId for the current state for the app */ @Nullable public LocusId getLocusId() { @@ -120,6 +127,28 @@ public final class DirectAction implements Parcelable { } @Override + public int hashCode() { + return mID.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other == null) { + return false; + } + + if (other == this) { + return true; + } + + if (getClass() != other.getClass()) { + return false; + } + + return mID.equals(((DirectAction) other).mID); + } + + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mTaskId); dest.writeStrongBinder(mActivityId); @@ -139,7 +168,8 @@ public final class DirectAction implements Parcelable { /** * Creates a new instance. * - * @param id The mandatory action id. + * @param id The mandatory action id which must be unique in the + * current application state. */ public Builder(@NonNull String id) { Preconditions.checkNotNull(id); @@ -147,7 +177,9 @@ public final class DirectAction implements Parcelable { } /** - * Sets the optional action extras. + * Sets the optional action extras. These extras are action specific + * and their semantics are open-ended potentially representing how + * the action is visualized, interpreted, what its arguments are, etc. * * @param extras The extras. * @return This builder. @@ -158,7 +190,9 @@ public final class DirectAction implements Parcelable { } /** - * Sets the optional locus id. + * Sets the optional locus id. This is an identifier of the application + * state from a user perspective. For example, a specific chat in a + * messaging app. * * @param locusId The locus id. * @return This builder. diff --git a/core/java/android/app/SharedElementCallback.java b/core/java/android/app/SharedElementCallback.java index 9fabfde5d06f..0287564bcc56 100644 --- a/core/java/android/app/SharedElementCallback.java +++ b/core/java/android/app/SharedElementCallback.java @@ -18,6 +18,7 @@ package android.app; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.graphics.Matrix; import android.graphics.RectF; @@ -49,6 +50,7 @@ public abstract class SharedElementCallback { private static final String BUNDLE_SNAPSHOT_BITMAP = "sharedElement:snapshot:bitmap"; private static final String BUNDLE_SNAPSHOT_GRAPHIC_BUFFER = "sharedElement:snapshot:graphicBuffer"; + private static final String BUNDLE_SNAPSHOT_COLOR_SPACE = "sharedElement:snapshot:colorSpace"; private static final String BUNDLE_SNAPSHOT_IMAGE_SCALETYPE = "sharedElement:snapshot:imageScaleType"; private static final String BUNDLE_SNAPSHOT_IMAGE_MATRIX = "sharedElement:snapshot:imageMatrix"; @@ -186,6 +188,10 @@ public abstract class SharedElementCallback { } else { GraphicBuffer graphicBuffer = bitmap.createGraphicBufferHandle(); bundle.putParcelable(BUNDLE_SNAPSHOT_GRAPHIC_BUFFER, graphicBuffer); + ColorSpace cs = bitmap.getColorSpace(); + if (cs != null) { + bundle.putInt(BUNDLE_SNAPSHOT_COLOR_SPACE, cs.getId()); + } } bundle.putString(BUNDLE_SNAPSHOT_IMAGE_SCALETYPE, imageView.getScaleType().toString()); @@ -235,8 +241,13 @@ public abstract class SharedElementCallback { return null; } if (bitmap == null) { + ColorSpace colorSpace = null; + int colorSpaceId = bundle.getInt(BUNDLE_SNAPSHOT_COLOR_SPACE, 0); + if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) { + colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]); + } bitmap = Bitmap.wrapHardwareBuffer(HardwareBuffer.createFromGraphicBuffer(buffer), - null); + colorSpace); } ImageView imageView = new ImageView(context); view = imageView; diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index 97b9176d4a86..a4a97c4cac1d 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -19,6 +19,7 @@ package android.app; import android.annotation.UnsupportedAppUsage; import android.app.ActivityManager.TaskSnapshot; import android.content.ComponentName; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -155,6 +156,11 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override @UnsupportedAppUsage public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) throws RemoteException { + if (Binder.getCallingPid() != android.os.Process.myPid() + && snapshot != null && snapshot.getSnapshot() != null) { + // Preemptively clear any reference to the buffer + snapshot.getSnapshot().destroy(); + } } @Override diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 0d5a7633f305..3fca3118c9a0 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -699,6 +699,7 @@ public class AssistStructure implements Parcelable { static final int AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS = 0x100; static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_EMS = 0x200; static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_LENGTH = 0x400; + static final int AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID = 0x800; int mFlags; int mAutofillFlags; @@ -754,6 +755,9 @@ public class AssistStructure implements Parcelable { } else { mAutofillId = new AutofillId(autofillViewId); } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID) != 0) { + mAutofillId.setSessionId(in.readInt()); + } } if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE) != 0) { mAutofillType = in.readInt(); @@ -899,6 +903,9 @@ public class AssistStructure implements Parcelable { if (mAutofillId.isVirtualInt()) { autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID; } + if (mAutofillId.hasSession()) { + autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID; + } } if (mAutofillValue != null) { autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE; @@ -965,7 +972,9 @@ public class AssistStructure implements Parcelable { if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID) != 0) { out.writeInt(mAutofillId.getVirtualChildIntId()); } - // TODO(b/113593220): write session id as well + if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID) != 0) { + out.writeInt(mAutofillId.getSessionId()); + } } if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE) != 0) { out.writeInt(mAutofillType); diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index f8dc20e9af09..7fa436084246 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -198,6 +198,8 @@ public final class UsageStatsManager { /** @hide */ public static final int REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED = 0x000E; /** @hide */ + public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_START = 0x000F; + /** @hide */ public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001; @@ -997,6 +999,9 @@ public final class UsageStatsManager { case REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED: sb.append("-uss"); break; + case REASON_SUB_USAGE_FOREGROUND_SERVICE_START: + sb.append("-fss"); + break; } break; } diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index 442b23938ccd..1fe1b10005cf 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -22,23 +22,16 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; -import com.android.internal.annotations.GuardedBy; - import java.util.ArrayList; import java.util.List; -import java.util.concurrent.locks.ReentrantReadWriteLock; /** @@ -209,101 +202,32 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; - private Context mContext; - private ServiceListener mServiceListener; - private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); - @GuardedBy("mServiceLock") - private IBluetoothA2dp mService; private BluetoothAdapter mAdapter; - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - try { - mServiceLock.writeLock().lock(); - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.writeLock().unlock(); - } - } else { - try { - mServiceLock.readLock().lock(); - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.readLock().unlock(); - } - } + private final BluetoothProfileConnector<IBluetoothA2dp> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp", + IBluetoothA2dp.class.getName()) { + @Override + public IBluetoothA2dp getServiceInterface(IBinder service) { + return IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothA2dp proxy object for interacting with the local * Bluetooth A2DP service. */ - /*package*/ BluetoothA2dp(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothA2dp(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothA2dp.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } @UnsupportedAppUsage /*package*/ void close() { - mServiceListener = null; - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - try { - mServiceLock.writeLock().lock(); - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.writeLock().unlock(); - } + private IBluetoothA2dp getService() { + return mProfileConnector.getService(); } @Override @@ -333,17 +257,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.connect(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.connect(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -376,17 +298,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.disconnect(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.disconnect(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -397,17 +317,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getConnectedDevices(); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getConnectedDevices(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -418,17 +336,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getDevicesMatchingConnectionStates(states); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getDevicesMatchingConnectionStates(states); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -439,18 +355,16 @@ public final class BluetoothA2dp implements BluetoothProfile { public @BtProfileState int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getConnectionState(device); + return service.getConnectionState(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; - } finally { - mServiceLock.readLock().unlock(); } } @@ -480,18 +394,16 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && ((device == null) || isValidDevice(device))) { - return mService.setActiveDevice(device); + return service.setActiveDevice(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -511,17 +423,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public BluetoothDevice getActiveDevice() { if (VDBG) log("getActiveDevice()"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getActiveDevice(); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getActiveDevice(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return null; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return null; - } finally { - mServiceLock.readLock().unlock(); } } @@ -543,22 +453,20 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { return false; } - return mService.setPriority(device, priority); + return service.setPriority(device, priority); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -578,18 +486,16 @@ public final class BluetoothA2dp implements BluetoothProfile { public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getPriority(device); + return service.getPriority(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.PRIORITY_OFF; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.PRIORITY_OFF; - } finally { - mServiceLock.readLock().unlock(); } } @@ -602,17 +508,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean isAvrcpAbsoluteVolumeSupported() { if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.isAvrcpAbsoluteVolumeSupported(); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.isAvrcpAbsoluteVolumeSupported(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -625,15 +529,13 @@ public final class BluetoothA2dp implements BluetoothProfile { public void setAvrcpAbsoluteVolume(int volume) { if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - mService.setAvrcpAbsoluteVolume(volume); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + service.setAvrcpAbsoluteVolume(volume); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e); - } finally { - mServiceLock.readLock().unlock(); } } @@ -646,18 +548,16 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public boolean isA2dpPlaying(BluetoothDevice device) { try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.isA2dpPlaying(device); + return service.isA2dpPlaying(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -694,19 +594,17 @@ public final class BluetoothA2dp implements BluetoothProfile { public @Nullable BluetoothCodecStatus getCodecStatus(BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getCodecStatus(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getCodecStatus(device); } - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return null; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in getCodecStatus()", e); return null; - } finally { - mServiceLock.readLock().unlock(); } } @@ -723,17 +621,15 @@ public final class BluetoothA2dp implements BluetoothProfile { BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - mService.setCodecConfigPreference(device, codecConfig); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + service.setCodecConfigPreference(device, codecConfig); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e); return; - } finally { - mServiceLock.readLock().unlock(); } } @@ -772,21 +668,19 @@ public final class BluetoothA2dp implements BluetoothProfile { */ private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) { try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { if (enable) { - mService.enableOptionalCodecs(device); + service.enableOptionalCodecs(device); } else { - mService.disableOptionalCodecs(device); + service.disableOptionalCodecs(device); } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e); return; - } finally { - mServiceLock.readLock().unlock(); } } @@ -801,17 +695,15 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage public int supportsOptionalCodecs(BluetoothDevice device) { try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.supportsOptionalCodecs(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.supportsOptionalCodecs(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == 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(); } } @@ -826,17 +718,15 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage public int getOptionalCodecsEnabled(BluetoothDevice device) { try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.getOptionalCodecsEnabled(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.getOptionalCodecsEnabled(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == 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(); } } @@ -858,18 +748,16 @@ public final class BluetoothA2dp implements BluetoothProfile { Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value); return; } - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { - mService.setOptionalCodecsEnabled(device, value); + service.setOptionalCodecsEnabled(device, value); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == 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(); } } @@ -900,35 +788,6 @@ public final class BluetoothA2dp implements BluetoothProfile { } } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - try { - mServiceLock.writeLock().lock(); - mService = IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service)); - } finally { - mServiceLock.writeLock().unlock(); - } - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - try { - mServiceLock.writeLock().lock(); - mService = null; - } finally { - mServiceLock.writeLock().unlock(); - } - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP); - } - } - }; - private boolean isEnabled() { if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; return false; diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java index cb996f3381b7..5a8055a29cfc 100755 --- a/core/java/android/bluetooth/BluetoothA2dpSink.java +++ b/core/java/android/bluetooth/BluetoothA2dpSink.java @@ -17,14 +17,10 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -125,93 +121,31 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public static final String EXTRA_AUDIO_CONFIG = "android.bluetooth.a2dp-sink.profile.extra.AUDIO_CONFIG"; - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothA2dpSink mService; private BluetoothAdapter mAdapter; - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private final BluetoothProfileConnector<IBluetoothA2dpSink> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.A2DP_SINK, + "BluetoothA2dpSink", IBluetoothA2dpSink.class.getName()) { + @Override + public IBluetoothA2dpSink getServiceInterface(IBinder service) { + return IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothA2dp proxy object for interacting with the local * Bluetooth A2DP service. */ - /*package*/ BluetoothA2dpSink(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothA2dpSink(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothA2dpSink.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /*package*/ void close() { - mServiceListener = null; - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private IBluetoothA2dpSink getService() { + return mProfileConnector.getService(); } @Override @@ -242,7 +176,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -283,7 +217,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -302,7 +236,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @Override public List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -321,7 +255,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -340,7 +274,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -366,7 +300,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { if (VDBG) log("getAudioConfig(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getAudioConfig(device); @@ -396,7 +330,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -428,7 +362,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -449,7 +383,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @param device BluetoothDevice device */ public boolean isA2dpPlaying(BluetoothDevice device) { - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.isA2dpPlaying(device); @@ -488,25 +422,6 @@ public final class BluetoothA2dpSink implements BluetoothProfile { } } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.A2DP_SINK, - BluetoothA2dpSink.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP_SINK); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java index c447868d6f0c..4e7e4415c54d 100644 --- a/core/java/android/bluetooth/BluetoothAvrcpController.java +++ b/core/java/android/bluetooth/BluetoothAvrcpController.java @@ -16,14 +16,10 @@ package android.bluetooth; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -80,93 +76,32 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public static final String EXTRA_PLAYER_SETTING = "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING"; - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothAvrcpController mService; private BluetoothAdapter mAdapter; - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private final BluetoothProfileConnector<IBluetoothAvrcpController> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.AVRCP_CONTROLLER, + "BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) { + @Override + public IBluetoothAvrcpController getServiceInterface(IBinder service) { + return IBluetoothAvrcpController.Stub.asInterface( + Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothAvrcpController proxy object for interacting with the local * Bluetooth AVRCP service. */ - /*package*/ BluetoothAvrcpController(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothAvrcpController(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothAvrcpController.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /*package*/ void close() { - mServiceListener = null; - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private IBluetoothAvrcpController getService() { + return mProfileConnector.getService(); } @Override @@ -180,7 +115,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @Override public List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -199,7 +135,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -218,7 +155,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -239,7 +177,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) { if (DBG) Log.d(TAG, "getPlayerSettings"); BluetoothAvrcpPlayerSettings settings = null; - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { settings = service.getPlayerSettings(device); @@ -257,7 +196,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { */ public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) { if (DBG) Log.d(TAG, "setPlayerApplicationSetting"); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { return service.setPlayerApplicationSetting(plAppSetting); @@ -277,7 +217,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) { Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + keyState); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { service.sendGroupNavigationCmd(device, keyCode, keyState); @@ -290,25 +231,6 @@ public final class BluetoothAvrcpController implements BluetoothProfile { if (service == null) Log.w(TAG, "Proxy not attached to service"); } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER, - BluetoothAvrcpController.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.AVRCP_CONTROLLER); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 8d9d340ee68b..9862a63ef238 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -337,19 +337,9 @@ public final class BluetoothHeadset implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); doUnbind(); } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } + doBind(); } } }; @@ -374,24 +364,32 @@ public final class BluetoothHeadset implements BluetoothProfile { doBind(); } - boolean doBind() { - try { - return mAdapter.getBluetoothManager().bindBluetoothProfileService( - BluetoothProfile.HEADSET, mConnection); - } catch (RemoteException e) { - Log.e(TAG, "Unable to bind HeadsetService", e); + private boolean doBind() { + synchronized (mConnection) { + if (mService == null) { + if (VDBG) Log.d(TAG, "Binding service..."); + try { + return mAdapter.getBluetoothManager().bindBluetoothProfileService( + BluetoothProfile.HEADSET, mConnection); + } catch (RemoteException e) { + Log.e(TAG, "Unable to bind HeadsetService", e); + } + } } return false; } - void doUnbind() { + private void doUnbind() { synchronized (mConnection) { if (mService != null) { + if (VDBG) Log.d(TAG, "Unbinding service..."); try { mAdapter.getBluetoothManager().unbindBluetoothProfileService( BluetoothProfile.HEADSET, mConnection); } catch (RemoteException e) { Log.e(TAG, "Unable to unbind HeadsetService", e); + } finally { + mService = null; } } } @@ -411,8 +409,8 @@ public final class BluetoothHeadset implements BluetoothProfile { if (mgr != null) { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); + } catch (RemoteException re) { + Log.e(TAG, "", re); } } mServiceListener = null; @@ -1169,7 +1167,7 @@ public final class BluetoothHeadset implements BluetoothProfile { @Override public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; + doUnbind(); mHandler.sendMessage(mHandler.obtainMessage( MESSAGE_HEADSET_SERVICE_DISCONNECTED)); } diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java index 549c1faddd90..05833b5f571d 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java @@ -17,15 +17,11 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -367,73 +363,22 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { public static final int CALL_ACCEPT_HOLD = 1; public static final int CALL_ACCEPT_TERMINATE = 2; - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothHeadsetClient mService; private BluetoothAdapter mAdapter; - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { + private final BluetoothProfileConnector<IBluetoothHeadsetClient> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HEADSET_CLIENT, + "BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) { @Override - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - Intent intent = new Intent( - IBluetoothHeadsetClient.class.getName()); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + public IBluetoothHeadsetClient getServiceInterface(IBinder service) { + return IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothHeadsetClient proxy object. */ - /*package*/ BluetoothHeadsetClient(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothHeadsetClient(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothHeadsetClient.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /** @@ -444,27 +389,11 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ /*package*/ void close() { if (VDBG) log("close()"); + mProfileConnector.disconnect(); + } - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } - - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothHeadsetClient getService() { + return mProfileConnector.getService(); } /** @@ -481,7 +410,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -504,7 +434,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -525,7 +456,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -548,7 +480,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -570,7 +503,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -590,7 +524,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -612,7 +547,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -638,7 +574,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.startVoiceRecognition(device); @@ -663,7 +600,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.stopVoiceRecognition(device); @@ -683,7 +621,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getCurrentCalls(device); @@ -703,7 +642,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public Bundle getCurrentAgEvents(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getCurrentAgEvents(device); @@ -727,7 +667,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.acceptCall(device, flag); @@ -748,7 +689,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean holdCall(BluetoothDevice device) { if (DBG) log("holdCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.holdCall(device); @@ -774,7 +716,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.rejectCall(device); @@ -804,7 +747,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { if (DBG) log("terminateCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.terminateCall(device, call); @@ -832,7 +776,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean enterPrivateMode(BluetoothDevice device, int index) { if (DBG) log("enterPrivateMode()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.enterPrivateMode(device, index); @@ -859,7 +804,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean explicitCallTransfer(BluetoothDevice device) { if (DBG) log("explicitCallTransfer()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.explicitCallTransfer(device); @@ -882,7 +828,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { if (DBG) log("dial()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.dial(device, number); @@ -906,7 +853,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean sendDTMF(BluetoothDevice device, byte code) { if (DBG) log("sendDTMF()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendDTMF(device, code); @@ -932,7 +880,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean getLastVoiceTagNumber(BluetoothDevice device) { if (DBG) log("getLastVoiceTagNumber()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getLastVoiceTagNumber(device); @@ -952,7 +901,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getAudioState(device); @@ -975,7 +925,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { service.setAudioRouteAllowed(device, allowed); @@ -997,7 +948,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean getAudioRouteAllowed(BluetoothDevice device) { if (VDBG) log("getAudioRouteAllowed"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getAudioRouteAllowed(device); @@ -1021,7 +973,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ public boolean connectAudio(BluetoothDevice device) { - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.connectAudio(device); @@ -1045,7 +998,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ public boolean disconnectAudio(BluetoothDevice device) { - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.disconnectAudio(device); @@ -1066,7 +1020,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return bundle of AG features; null if no service or AG not connected */ public Bundle getCurrentAgFeatures(BluetoothDevice device) { - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getCurrentAgFeatures(device); @@ -1080,29 +1035,6 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { return null; } - - private final ServiceConnection mConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service)); - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT, - BluetoothHeadsetClient.this); - } - } - - @Override - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET_CLIENT); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java index d6edb90ca27b..60fb6fb122e3 100644 --- a/core/java/android/bluetooth/BluetoothHearingAid.java +++ b/core/java/android/bluetooth/BluetoothHearingAid.java @@ -22,21 +22,14 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; -import com.android.internal.annotations.GuardedBy; - import java.util.ArrayList; import java.util.List; -import java.util.concurrent.locks.ReentrantReadWriteLock; /** * This class provides the public APIs to control the Hearing Aid profile. @@ -129,97 +122,31 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID; - private Context mContext; - private ServiceListener mServiceListener; - private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); - @GuardedBy("mServiceLock") - private IBluetoothHearingAid mService; private BluetoothAdapter mAdapter; - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - try { - mServiceLock.writeLock().lock(); - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.writeLock().unlock(); - } - } else { - try { - mServiceLock.readLock().lock(); - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.readLock().unlock(); - } - } + private final BluetoothProfileConnector<IBluetoothHearingAid> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HEARING_AID, + "BluetoothHearingAid", IBluetoothHearingAid.class.getName()) { + @Override + public IBluetoothHearingAid getServiceInterface(IBinder service) { + return IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothHearingAid proxy object for interacting with the local * Bluetooth Hearing Aid service. */ - /*package*/ BluetoothHearingAid(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothHearingAid(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - } - - void doBind() { - Intent intent = new Intent(IBluetoothHearingAid.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent); - return; - } + mProfileConnector.connect(context, listener); } /*package*/ void close() { - mServiceListener = null; - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - try { - mServiceLock.writeLock().lock(); - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.writeLock().unlock(); - } + private IBluetoothHearingAid getService() { + return mProfileConnector.getService(); } /** @@ -241,18 +168,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.connect(device); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.connect(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -283,18 +208,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.disconnect(device); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.disconnect(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -304,18 +227,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { @Override public @NonNull List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getConnectedDevices(); + if (service != null && isEnabled()) { + return service.getConnectedDevices(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -326,18 +247,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates( @NonNull int[] states) { if (VDBG) log("getDevicesMatchingStates()"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getDevicesMatchingConnectionStates(states); + if (service != null && isEnabled()) { + return service.getDevicesMatchingConnectionStates(states); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -348,19 +267,17 @@ public final class BluetoothHearingAid implements BluetoothProfile { public @BluetoothProfile.BtProfileState int getConnectionState( @NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getConnectionState(device); + return service.getConnectionState(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; - } finally { - mServiceLock.readLock().unlock(); } } @@ -388,20 +305,18 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && ((device == null) || isValidDevice(device))) { - mService.setActiveDevice(device); + service.setActiveDevice(device); return true; } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -419,18 +334,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH) public List<BluetoothDevice> getActiveDevices() { if (VDBG) log("getActiveDevices()"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getActiveDevices(); + if (service != null && isEnabled()) { + return service.getActiveDevices(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<>(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<>(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -451,23 +364,21 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { return false; } - return mService.setPriority(device, priority); + return service.setPriority(device, priority); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -485,19 +396,17 @@ public final class BluetoothHearingAid implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getPriority(device); + return service.getPriority(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.PRIORITY_OFF; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.PRIORITY_OFF; - } finally { - mServiceLock.readLock().unlock(); } } @@ -536,18 +445,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getVolume()"); } + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getVolume(); + if (service != null && isEnabled()) { + return service.getVolume(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return 0; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return 0; - } finally { - mServiceLock.readLock().unlock(); } } @@ -567,21 +474,18 @@ public final class BluetoothHearingAid implements BluetoothProfile { public void adjustVolume(int direction) { if (DBG) log("adjustVolume(" + direction + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); return; } if (!isEnabled()) return; - mService.adjustVolume(direction); + service.adjustVolume(direction); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - } finally { - mServiceLock.readLock().unlock(); } } @@ -594,20 +498,18 @@ public final class BluetoothHearingAid implements BluetoothProfile { public void setVolume(int volume) { if (DBG) Log.d(TAG, "setVolume(" + volume + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); return; } if (!isEnabled()) return; - mService.setVolume(volume); + service.setVolume(volume); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - } finally { - mServiceLock.readLock().unlock(); } } @@ -623,21 +525,19 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getCustomerId(" + device + ")"); } + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); return HI_SYNC_ID_INVALID; } if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID; - return mService.getHiSyncId(device); + return service.getHiSyncId(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return HI_SYNC_ID_INVALID; - } finally { - mServiceLock.readLock().unlock(); } } @@ -653,19 +553,17 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getDeviceSide(" + device + ")"); } + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getDeviceSide(device); + return service.getDeviceSide(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return SIDE_LEFT; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return SIDE_LEFT; - } finally { - mServiceLock.readLock().unlock(); } } @@ -681,52 +579,20 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getDeviceMode(" + device + ")"); } + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getDeviceMode(device); + return service.getDeviceMode(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return MODE_MONAURAL; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return MODE_MONAURAL; - } finally { - mServiceLock.readLock().unlock(); } } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - try { - mServiceLock.writeLock().lock(); - mService = IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service)); - } finally { - mServiceLock.writeLock().unlock(); - } - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HEARING_AID, - BluetoothHearingAid.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - try { - mServiceLock.writeLock().lock(); - mService = null; - } finally { - mServiceLock.writeLock().unlock(); - } - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HEARING_AID); - } - } - }; - private boolean isEnabled() { if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; return false; diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java index e44f36e90c75..e9b0be2c4cd6 100644 --- a/core/java/android/bluetooth/BluetoothHidDevice.java +++ b/core/java/android/bluetooth/BluetoothHidDevice.java @@ -18,13 +18,10 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -328,11 +325,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { } } - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothHidDevice mService; - private BluetoothAdapter mAdapter; - private static class CallbackWrapper extends IBluetoothHidDeviceCallback.Stub { private final Executor mExecutor; @@ -386,114 +378,33 @@ public final class BluetoothHidDevice implements BluetoothProfile { } } - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - - public void onBluetoothStateChange(boolean up) { - Log.d(TAG, "onBluetoothStateChange: up=" + up); - synchronized (mConnection) { - if (up) { - try { - if (mService == null) { - Log.d(TAG, "Binding HID Device service..."); - doBind(); - } - } catch (IllegalStateException e) { - Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev " - + "service: ", e); - } catch (SecurityException e) { - Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev " - + "service: ", e); - } - } else { - Log.d(TAG, "Unbinding service..."); - doUnbind(); - } - } - } - }; - - private final ServiceConnection mConnection = - new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - Log.d(TAG, "onServiceConnected()"); - mService = IBluetoothHidDevice.Stub.asInterface(service); - if (mServiceListener != null) { - mServiceListener.onServiceConnected( - BluetoothProfile.HID_DEVICE, BluetoothHidDevice.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - Log.d(TAG, "onServiceDisconnected()"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE); - } + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector<IBluetoothHidDevice> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HID_DEVICE, + "BluetoothHidDevice", IBluetoothHidDevice.class.getName()) { + @Override + public IBluetoothHidDevice getServiceInterface(IBinder service) { + return IBluetoothHidDevice.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; BluetoothHidDevice(Context context, ServiceListener listener) { - mContext = context; - mServiceListener = listener; mAdapter = BluetoothAdapter.getDefaultAdapter(); - - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - e.printStackTrace(); - } - } - - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothHidDevice.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent); - return false; - } - Log.d(TAG, "Bound to HID Device Service"); - return true; - } - - void doUnbind() { - if (mService != null) { - mService = null; - try { - mContext.unbindService(mConnection); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Unable to unbind HidDevService", e); - } - } + mProfileConnector.connect(context, listener); } void close() { - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - e.printStackTrace(); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - doUnbind(); - } - mServiceListener = null; + private IBluetoothHidDevice getService() { + return mProfileConnector.getService(); } /** {@inheritDoc} */ @Override public List<BluetoothDevice> getConnectedDevices() { - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { return service.getConnectedDevices(); @@ -510,7 +421,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** {@inheritDoc} */ @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { return service.getDevicesMatchingConnectionStates(states); @@ -527,7 +438,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** {@inheritDoc} */ @Override public int getConnectionState(BluetoothDevice device) { - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { return service.getConnectionState(device); @@ -584,7 +495,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { throw new IllegalArgumentException("callback parameter cannot be null"); } - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { CallbackWrapper cbw = new CallbackWrapper(executor, callback); @@ -612,7 +523,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean unregisterApp() { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.unregisterApp(); @@ -637,7 +548,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean sendReport(BluetoothDevice device, int id, byte[] data) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.sendReport(device, id, data); @@ -663,7 +574,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.replyReport(device, type, id, data); @@ -687,7 +598,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean reportError(BluetoothDevice device, byte error) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.reportError(device, error); @@ -708,7 +619,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * {@hide} */ public String getUserAppName() { - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { @@ -734,7 +645,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean connect(BluetoothDevice device) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.connect(device); @@ -758,7 +669,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean disconnect(BluetoothDevice device) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.disconnect(device); diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java index 7c925a1cb5ef..4afb382c0660 100644 --- a/core/java/android/bluetooth/BluetoothHidHost.java +++ b/core/java/android/bluetooth/BluetoothHidHost.java @@ -18,14 +18,10 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -220,97 +216,32 @@ public final class BluetoothHidHost implements BluetoothProfile { public static final String EXTRA_IDLE_TIME = "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME"; - private Context mContext; - private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private volatile IBluetoothHidHost mService; - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private final BluetoothProfileConnector<IBluetoothHidHost> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HID_HOST, + "BluetoothHidHost", IBluetoothHidHost.class.getName()) { + @Override + public IBluetoothHidHost getServiceInterface(IBinder service) { + return IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothHidHost proxy object for interacting with the local * Bluetooth Service which handles the InputDevice profile */ - /*package*/ BluetoothHidHost(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothHidHost(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothHidHost.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /*package*/ void close() { if (VDBG) log("close()"); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothHidHost getService() { + return mProfileConnector.getService(); } /** @@ -334,7 +265,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -374,7 +305,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -393,7 +324,7 @@ public final class BluetoothHidHost implements BluetoothProfile { @Override public List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -412,7 +343,7 @@ public final class BluetoothHidHost implements BluetoothProfile { @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -431,7 +362,7 @@ public final class BluetoothHidHost implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -461,7 +392,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -493,7 +424,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -506,26 +437,6 @@ public final class BluetoothHidHost implements BluetoothProfile { return BluetoothProfile.PRIORITY_OFF; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service)); - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HID_HOST, - BluetoothHidHost.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HID_HOST); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } @@ -545,7 +456,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean virtualUnplug(BluetoothDevice device) { if (DBG) log("virtualUnplug(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.virtualUnplug(device); @@ -571,7 +482,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean getProtocolMode(BluetoothDevice device) { if (VDBG) log("getProtocolMode(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getProtocolMode(device); @@ -595,7 +506,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean setProtocolMode(BluetoothDevice device, int protocolMode) { if (DBG) log("setProtocolMode(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setProtocolMode(device, protocolMode); @@ -626,7 +537,7 @@ public final class BluetoothHidHost implements BluetoothProfile { log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize); } - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getReport(device, reportType, reportId, bufferSize); @@ -652,7 +563,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean setReport(BluetoothDevice device, byte reportType, String report) { if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setReport(device, reportType, report); @@ -677,7 +588,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean sendData(BluetoothDevice device, String report) { if (DBG) log("sendData(" + device + "), report=" + report); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendData(device, report); @@ -701,7 +612,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean getIdleTime(BluetoothDevice device) { if (DBG) log("getIdletime(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getIdleTime(device); @@ -726,7 +637,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean setIdleTime(BluetoothDevice device, byte idleTime) { if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setIdleTime(device, idleTime); diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java index fc5f830a8940..dd2f150ad4eb 100644 --- a/core/java/android/bluetooth/BluetoothMap.java +++ b/core/java/android/bluetooth/BluetoothMap.java @@ -17,14 +17,10 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -45,11 +41,6 @@ public final class BluetoothMap implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; - private volatile IBluetoothMap mService; - private final Context mContext; - private ServiceListener mServiceListener; - private BluetoothAdapter mAdapter; - /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; @@ -58,64 +49,23 @@ public final class BluetoothMap implements BluetoothProfile { /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector<IBluetoothMap> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.MAP, + "BluetoothMap", IBluetoothMap.class.getName()) { + @Override + public IBluetoothMap getServiceInterface(IBinder service) { + return IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothMap proxy object. */ - /*package*/ BluetoothMap(Context context, ServiceListener l) { + /*package*/ BluetoothMap(Context context, ServiceListener listener) { if (DBG) Log.d(TAG, "Create BluetoothMap proxy object"); - mContext = context; - mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothMap.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } protected void finalize() throws Throwable { @@ -133,26 +83,11 @@ public final class BluetoothMap implements BluetoothProfile { * are ok. */ public synchronized void close() { - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothMap getService() { + return mProfileConnector.getService(); } /** @@ -163,7 +98,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getState() { if (VDBG) log("getState()"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null) { try { return service.getState(); @@ -185,7 +120,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null) { try { return service.getClient(); @@ -206,7 +141,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null) { try { return service.isConnected(device); @@ -238,7 +173,7 @@ public final class BluetoothMap implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -279,7 +214,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public List<BluetoothDevice> getConnectedDevices() { if (DBG) log("getConnectedDevices()"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -299,7 +234,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -319,7 +254,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -345,7 +280,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -374,7 +309,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -387,24 +322,6 @@ public final class BluetoothMap implements BluetoothProfile { return PRIORITY_OFF; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) log("Proxy object connected"); - mService = IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.MAP, BluetoothMap.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) log("Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.MAP); - } - } - }; - private static void log(String msg) { Log.d(TAG, msg); } diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java index 1c82e1984b66..ec0180c5adde 100644 --- a/core/java/android/bluetooth/BluetoothMapClient.java +++ b/core/java/android/bluetooth/BluetoothMapClient.java @@ -18,14 +18,11 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; import android.app.PendingIntent; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.net.Uri; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -61,11 +58,6 @@ public final class BluetoothMapClient implements BluetoothProfile { public static final String EXTRA_SENDER_CONTACT_NAME = "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME"; - private volatile IBluetoothMapClient mService; - private final Context mContext; - private ServiceListener mServiceListener; - private BluetoothAdapter mAdapter; - /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; @@ -76,64 +68,23 @@ public final class BluetoothMapClient implements BluetoothProfile { private static final int UPLOADING_FEATURE_BITMASK = 0x08; - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector<IBluetoothMapClient> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT, + "BluetoothMapClient", IBluetoothMapClient.class.getName()) { + @Override + public IBluetoothMapClient getServiceInterface(IBinder service) { + return IBluetoothMapClient.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothMapClient proxy object. */ - /*package*/ BluetoothMapClient(Context context, ServiceListener l) { + /*package*/ BluetoothMapClient(Context context, ServiceListener listener) { if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object"); - mContext = context; - mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothMapClient.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } protected void finalize() throws Throwable { @@ -151,26 +102,11 @@ public final class BluetoothMapClient implements BluetoothProfile { * are ok. */ public void close() { - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothMapClient getService() { + return mProfileConnector.getService(); } /** @@ -180,7 +116,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) Log.d(TAG, "isConnected(" + device + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null) { try { return service.isConnected(device); @@ -200,7 +136,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null) { try { return service.connect(device); @@ -222,7 +158,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) Log.d(TAG, "disconnect(" + device + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -242,7 +178,7 @@ public final class BluetoothMapClient implements BluetoothProfile { @Override public List<BluetoothDevice> getConnectedDevices() { if (DBG) Log.d(TAG, "getConnectedDevices()"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -263,7 +199,7 @@ public final class BluetoothMapClient implements BluetoothProfile { @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (DBG) Log.d(TAG, "getDevicesMatchingStates()"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -284,7 +220,7 @@ public final class BluetoothMapClient implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (DBG) Log.d(TAG, "getConnectionState(" + device + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -308,7 +244,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -337,7 +273,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) Log.d(TAG, "getPriority(" + device + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -366,7 +302,7 @@ public final class BluetoothMapClient implements BluetoothProfile { public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message, PendingIntent sentIntent, PendingIntent deliveredIntent) { if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent); @@ -386,7 +322,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean getUnreadMessages(BluetoothDevice device) { if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getUnreadMessages(device); @@ -406,34 +342,16 @@ public final class BluetoothMapClient implements BluetoothProfile { * MapSupportedFeatures field is set. False is returned otherwise. */ public boolean isUploadingSupported(BluetoothDevice device) { + final IBluetoothMapClient service = getService(); try { - return (mService != null && isEnabled() && isValidDevice(device)) - && ((mService.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0); + return (service != null && isEnabled() && isValidDevice(device)) + && ((service.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0); } catch (RemoteException e) { Log.e(TAG, e.getMessage()); } return false; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothMapClient.Stub.asInterface(service); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT, - BluetoothMapClient.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.MAP_CLIENT); - } - } - }; - private boolean isEnabled() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true; diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java index 8923d734c844..fb78789ba8ad 100644 --- a/core/java/android/bluetooth/BluetoothPan.java +++ b/core/java/android/bluetooth/BluetoothPan.java @@ -19,14 +19,10 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -122,108 +118,42 @@ public final class BluetoothPan implements BluetoothProfile { */ public static final int PAN_OPERATION_SUCCESS = 1004; - private Context mContext; - private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private volatile IBluetoothPan mPanService; + private final BluetoothProfileConnector<IBluetoothPan> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.PAN, + "BluetoothPan", IBluetoothPan.class.getName()) { + @Override + public IBluetoothPan getServiceInterface(IBinder service) { + return IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service)); + } + }; + /** * Create a BluetoothPan proxy object for interacting with the local * Bluetooth Service which handles the Pan profile */ @UnsupportedAppUsage - /*package*/ BluetoothPan(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothPan(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - try { - mAdapter.getBluetoothManager().registerStateChangeCallback(mStateChangeCallback); - } catch (RemoteException re) { - Log.w(TAG, "Unable to register BluetoothStateChangeCallback", re); - } - if (VDBG) Log.d(TAG, "BluetoothPan() call bindService"); - doBind(); - } - - @UnsupportedAppUsage - boolean doBind() { - Intent intent = new Intent(IBluetoothPan.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } @UnsupportedAppUsage /*package*/ void close() { if (VDBG) log("close()"); + mProfileConnector.disconnect(); + } - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mStateChangeCallback); - } catch (RemoteException re) { - Log.w(TAG, "Unable to unregister BluetoothStateChangeCallback", re); - } - } - - synchronized (mConnection) { - if (mPanService != null) { - try { - mPanService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothPan getService() { + return mProfileConnector.getService(); } + protected void finalize() { close(); } - private final IBluetoothStateChangeCallback mStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - - @Override - public void onBluetoothStateChange(boolean on) { - // Handle enable request to bind again. - Log.d(TAG, "onBluetoothStateChange on: " + on); - if (on) { - try { - if (mPanService == null) { - if (VDBG) Log.d(TAG, "onBluetoothStateChange calling doBind()"); - doBind(); - } - - } catch (IllegalStateException e) { - Log.e(TAG, "onBluetoothStateChange: could not bind to PAN service: ", - e); - - } catch (SecurityException e) { - Log.e(TAG, "onBluetoothStateChange: could not bind to PAN service: ", - e); - } - } else { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mPanService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - } - }; - /** * Initiate connection to a profile of the remote bluetooth device. * @@ -244,7 +174,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -285,7 +215,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -304,7 +234,7 @@ public final class BluetoothPan implements BluetoothProfile { @Override public List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -323,7 +253,7 @@ public final class BluetoothPan implements BluetoothProfile { @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -342,7 +272,7 @@ public final class BluetoothPan implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -358,7 +288,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public void setBluetoothTethering(boolean value) { if (DBG) log("setBluetoothTethering(" + value + ")"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { service.setBluetoothTethering(value); @@ -371,7 +301,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { return service.isTetheringOn(); @@ -382,25 +312,6 @@ public final class BluetoothPan implements BluetoothProfile { return false; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected"); - mPanService = IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.PAN, - BluetoothPan.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "BluetoothPAN Proxy object disconnected"); - mPanService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.PAN); - } - } - }; - @UnsupportedAppUsage private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java index 359bec686a8a..d94c65742e6c 100644 --- a/core/java/android/bluetooth/BluetoothPbap.java +++ b/core/java/android/bluetooth/BluetoothPbap.java @@ -118,28 +118,9 @@ public class BluetoothPbap implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { log("onBluetoothStateChange: up=" + up); if (!up) { - log("Unbinding service..."); - synchronized (mConnection) { - try { - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } + doUnbind(); } else { - synchronized (mConnection) { - try { - if (mService == null) { - log("Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } + doBind(); } } }; @@ -155,25 +136,51 @@ public class BluetoothPbap implements BluetoothProfile { if (mgr != null) { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); + } catch (RemoteException re) { + Log.e(TAG, "", re); } } doBind(); } boolean doBind() { - Intent intent = new Intent(IBluetoothPbap.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent); - return false; + synchronized (mConnection) { + try { + if (mService == null) { + log("Binding service..."); + Intent intent = new Intent(IBluetoothPbap.class.getName()); + ComponentName comp = intent.resolveSystemService( + mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + UserHandle.CURRENT_OR_SELF)) { + Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent); + return false; + } + } + } catch (SecurityException se) { + Log.e(TAG, "", se); + return false; + } } return true; } + private void doUnbind() { + synchronized (mConnection) { + if (mService != null) { + log("Unbinding service..."); + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException ie) { + Log.e(TAG, "", ie); + } finally { + mService = null; + } + } + } + } + protected void finalize() throws Throwable { try { close(); @@ -193,21 +200,11 @@ public class BluetoothPbap implements BluetoothProfile { if (mgr != null) { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } - - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } + } catch (RemoteException re) { + Log.e(TAG, "", re); } } + doUnbind(); mServiceListener = null; } @@ -313,7 +310,7 @@ public class BluetoothPbap implements BluetoothProfile { public void onServiceDisconnected(ComponentName className) { log("Proxy object disconnected"); - mService = null; + doUnbind(); if (mServiceListener != null) { mServiceListener.onServiceDisconnected(); } diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java index cbc96c073338..d70e1e7f3f5f 100644 --- a/core/java/android/bluetooth/BluetoothPbapClient.java +++ b/core/java/android/bluetooth/BluetoothPbapClient.java @@ -16,14 +16,10 @@ package android.bluetooth; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -43,11 +39,6 @@ public final class BluetoothPbapClient implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED"; - private volatile IBluetoothPbapClient mService; - private final Context mContext; - private ServiceListener mServiceListener; - private BluetoothAdapter mAdapter; - /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; @@ -56,72 +47,25 @@ public final class BluetoothPbapClient implements BluetoothProfile { /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) { - Log.d(TAG, "onBluetoothStateChange: PBAP CLIENT up=" + up); - } - if (!up) { - if (VDBG) { - Log.d(TAG, "Unbinding service..."); - } - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) { - Log.d(TAG, "Binding service..."); - } - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector<IBluetoothPbapClient> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.PBAP_CLIENT, + "BluetoothPbapClient", IBluetoothPbapClient.class.getName()) { + @Override + public IBluetoothPbapClient getServiceInterface(IBinder service) { + return IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothPbapClient proxy object. */ - BluetoothPbapClient(Context context, ServiceListener l) { + BluetoothPbapClient(Context context, ServiceListener listener) { if (DBG) { Log.d(TAG, "Create BluetoothPbapClient proxy object"); } - mContext = context; - mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - doBind(); - } - - private boolean doBind() { - Intent intent = new Intent(IBluetoothPbapClient.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth PBAP Client Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } protected void finalize() throws Throwable { @@ -139,26 +83,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { * are ok. */ public synchronized void close() { - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothPbapClient getService() { + return mProfileConnector.getService(); } /** @@ -174,7 +103,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("connect(" + device + ") for PBAP Client."); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -199,7 +128,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("disconnect(" + device + ")" + new Exception()); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { service.disconnect(device); @@ -226,7 +155,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getConnectedDevices()"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -251,7 +180,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getDevicesMatchingStates()"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -276,7 +205,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getConnectionState(" + device + ")"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -291,29 +220,6 @@ public final class BluetoothPbapClient implements BluetoothProfile { return BluetoothProfile.STATE_DISCONNECTED; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) { - log("Proxy object connected"); - } - mService = IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT, - BluetoothPbapClient.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) { - log("Proxy object disconnected"); - } - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.PBAP_CLIENT); - } - } - }; - private static void log(String msg) { Log.d(TAG, msg); } @@ -346,7 +252,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("setPriority(" + device + ", " + priority + ")"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -379,7 +285,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (VDBG) { log("getPriority(" + device + ")"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); diff --git a/core/java/android/bluetooth/BluetoothProfileConnector.java b/core/java/android/bluetooth/BluetoothProfileConnector.java new file mode 100644 index 000000000000..d9987249a6e2 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothProfileConnector.java @@ -0,0 +1,166 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; + +/** + * Connector for Bluetooth profile proxies to bind manager service and + * profile services + * @param <T> The Bluetooth profile interface for this connection. + * @hide + */ +public abstract class BluetoothProfileConnector<T> { + private int mProfileId; + private BluetoothProfile.ServiceListener mServiceListener; + private BluetoothProfile mProfileProxy; + private Context mContext; + private String mProfileName; + private String mServiceName; + private volatile T mService; + + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (up) { + doBind(); + } else { + doUnbind(); + } + } + }; + + private final ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + logDebug("Proxy object connected"); + mService = getServiceInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(mProfileId, mProfileProxy); + } + } + + public void onServiceDisconnected(ComponentName className) { + logDebug("Proxy object disconnected"); + doUnbind(); + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP); + } + } + }; + + BluetoothProfileConnector(BluetoothProfile profile, int profileId, String profileName, + String serviceName) { + mProfileId = profileId; + mProfileProxy = profile; + mProfileName = profileName; + mServiceName = serviceName; + } + + private boolean doBind() { + synchronized (mConnection) { + if (mService == null) { + logDebug("Binding service..."); + try { + Intent intent = new Intent(mServiceName); + ComponentName comp = intent.resolveSystemService( + mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + UserHandle.CURRENT_OR_SELF)) { + logError("Could not bind to Bluetooth Service with " + intent); + return false; + } + } catch (SecurityException se) { + logError("Failed to bind service. " + se); + return false; + } + } + } + return true; + } + + private void doUnbind() { + synchronized (mConnection) { + if (mService != null) { + logDebug("Unbinding service..."); + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException ie) { + logError("Unable to unbind service: " + ie); + } finally { + mService = null; + } + } + } + } + + void connect(Context context, BluetoothProfile.ServiceListener listener) { + mContext = context; + mServiceListener = listener; + IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager(); + if (mgr != null) { + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + logError("Failed to register state change callback. " + re); + } + } + doBind(); + } + + void disconnect() { + mServiceListener = null; + IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + logError("Failed to unregister state change callback" + re); + } + } + doUnbind(); + } + + T getService() { + return mService; + } + + /** + * This abstract function is used to implement method to get the + * connected Bluetooth service interface. + * @param service the connected binder service. + * @return T the binder interface of {@code service}. + * @hide + */ + public abstract T getServiceInterface(IBinder service); + + private void logDebug(String log) { + Log.d(mProfileName, log); + } + + private void logError(String log) { + Log.e(mProfileName, log); + } +} diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java index ebf6bed54475..e0610c890559 100644 --- a/core/java/android/bluetooth/BluetoothSap.java +++ b/core/java/android/bluetooth/BluetoothSap.java @@ -17,14 +17,10 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -70,11 +66,6 @@ public final class BluetoothSap implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED"; - private volatile IBluetoothSap mService; - private final Context mContext; - private ServiceListener mServiceListener; - private BluetoothAdapter mAdapter; - /** * There was an error trying to obtain the state. * @@ -96,64 +87,23 @@ public final class BluetoothSap implements BluetoothProfile { */ public static final int RESULT_CANCELED = 2; - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector<IBluetoothSap> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.SAP, + "BluetoothSap", IBluetoothSap.class.getName()) { + @Override + public IBluetoothSap getServiceInterface(IBinder service) { + return IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothSap proxy object. */ - /*package*/ BluetoothSap(Context context, ServiceListener l) { + /*package*/ BluetoothSap(Context context, ServiceListener listener) { if (DBG) Log.d(TAG, "Create BluetoothSap proxy object"); - mContext = context; - mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothSap.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } protected void finalize() throws Throwable { @@ -173,26 +123,11 @@ public final class BluetoothSap implements BluetoothProfile { * @hide */ public synchronized void close() { - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothSap getService() { + return mProfileConnector.getService(); } /** @@ -204,7 +139,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getState() { if (VDBG) log("getState()"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null) { try { return service.getState(); @@ -227,7 +162,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null) { try { return service.getClient(); @@ -250,7 +185,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null) { try { return service.isConnected(device); @@ -285,7 +220,7 @@ public final class BluetoothSap implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -306,7 +241,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public List<BluetoothDevice> getConnectedDevices() { if (DBG) log("getConnectedDevices()"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -327,7 +262,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -348,7 +283,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -373,7 +308,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -399,7 +334,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -412,24 +347,6 @@ public final class BluetoothSap implements BluetoothProfile { return PRIORITY_OFF; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) log("Proxy object connected"); - mService = IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) log("Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.SAP); - } - } - }; - private static void log(String msg) { Log.d(TAG, msg); } diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 5f34f1b2bc0d..804677648d09 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -16,6 +16,8 @@ package android.content; +import static android.provider.DocumentsContract.EXTRA_ORIENTATION; + import android.accounts.Account; import android.annotation.IntDef; import android.annotation.NonNull; @@ -40,6 +42,7 @@ import android.graphics.Bitmap; import android.graphics.ImageDecoder; import android.graphics.ImageDecoder.ImageInfo; import android.graphics.ImageDecoder.Source; +import android.graphics.Matrix; import android.graphics.Point; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; @@ -56,6 +59,7 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.storage.StorageManager; +import android.system.Int32Ref; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; @@ -3563,9 +3567,14 @@ public abstract class ContentResolver implements ContentInterface { // Convert to Point, since that's what the API is defined as final Bundle opts = new Bundle(); opts.putParcelable(EXTRA_SIZE, Point.convert(size)); - - return ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> { - return content.openTypedAssetFile(uri, "image/*", opts, signal); + final Int32Ref orientation = new Int32Ref(0); + + Bitmap bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> { + final AssetFileDescriptor afd = content.openTypedAssetFile(uri, "image/*", opts, + signal); + final Bundle extras = afd.getExtras(); + orientation.value = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0; + return afd; }), (ImageDecoder decoder, ImageInfo info, Source source) -> { decoder.setAllocator(allocator); @@ -3581,6 +3590,20 @@ public abstract class ContentResolver implements ContentInterface { decoder.setTargetSampleSize(sample); } }); + + // Transform the bitmap if requested. We use a side-channel to + // communicate the orientation, since EXIF thumbnails don't contain + // the rotation flags of the original image. + if (orientation.value != 0) { + final int width = bitmap.getWidth(); + final int height = bitmap.getHeight(); + + final Matrix m = new Matrix(); + m.setRotate(orientation.value, width / 2, height / 2); + bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false); + } + + return bitmap; } /** {@hide} */ diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 3dd510c6a6e6..01123968fcbd 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3009,7 +3009,14 @@ public abstract class Context { /** * Variation of {@link #bindService} that, in the specific case of isolated * services, allows the caller to generate multiple instances of a service - * from a single component declaration. + * from a single component declaration. In other words, you can use this to bind + * to a service that has specified {@link android.R.attr#isolatedProcess} and, in + * addition to the existing behavior of running in an isolated process, you can + * also through the arguments here have the system bring up multiple concurrent + * processes hosting their own instances of that service. The <var>instanceName</var> + * you provide here identifies the different instances, and you can use + * {@link #updateServiceGroup(ServiceConnection, int, int)} to tell the system how it + * should manage each of these instances. * * @param service Identifies the service to connect to. The Intent must * specify an explicit component name. @@ -3027,6 +3034,8 @@ public abstract class Context { * @throws IllegalArgumentException If the instanceName is invalid. * * @see #bindService + * @see #updateServiceGroup + * @see android.R.attr#isolatedProcess */ public boolean bindIsolatedService(@RequiresPermission @NonNull Intent service, @BindServiceFlags int flags, @NonNull String instanceName, @@ -3082,10 +3091,16 @@ public abstract class Context { * are considered to be related. Supplying 0 reverts to the default behavior * of not grouping. * @param importance Additional importance of the processes within a group. Upon calling - * here, this will override any previous group that was set for that - * process. This fine-tunes process killing of all processes within - * a related groups -- higher importance values will be killed before - * lower ones. + * here, this will override any previous importance that was set for that + * process. The most important process is 0, and higher values are + * successively less important. You can view this as describing how + * to order the processes in an array, with the processes at the end of + * the array being the least important. This value has no meaning besides + * indicating how processes should be ordered in that array one after the + * other. This provides a way to fine-tune the system's process killing, + * guiding it to kill processes at the end of the array first. + * + * @see #bindIsolatedService */ public void updateServiceGroup(@NonNull ServiceConnection conn, int group, int importance) { diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index d2f0fb3cc768..a8815ec6cfaa 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1318,6 +1318,8 @@ public class PackageInstaller { public boolean isMultiPackage; /** {@hide} */ public boolean isStaged; + /** {@hide} */ + public long requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST; /** * Construct parameters for a new package install session. @@ -1350,6 +1352,7 @@ public class PackageInstaller { installerPackageName = source.readString(); isMultiPackage = source.readBoolean(); isStaged = source.readBoolean(); + requiredInstalledVersionCode = source.readLong(); } /** {@hide} */ @@ -1372,6 +1375,7 @@ public class PackageInstaller { ret.installerPackageName = installerPackageName; ret.isMultiPackage = isMultiPackage; ret.isStaged = isStaged; + ret.requiredInstalledVersionCode = requiredInstalledVersionCode; return ret; } @@ -1509,11 +1513,6 @@ public class PackageInstaller { * state of the permission can be determined only at install time and cannot be * changed on updated or at a later point via the package manager APIs. * - * <p>The whitelisted non-immutably restricted permissions would be added to - * the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER installer whitelist} - * while the immutably restricted permissions would be added to the {@link - * PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM system whitelist} - * * @see PackageManager#addWhitelistedRestrictedPermission(String, String, int) * @see PackageManager#removeWhitelistedRestrictedPermission(String, String, int) */ @@ -1567,6 +1566,19 @@ public class PackageInstaller { } } + /** + * Require the given version of the package be installed. + * The install will only be allowed if the existing version code of + * the package installed on the device matches the given version code. + * Use {@link * PackageManager#VERSION_CODE_HIGHEST} to allow + * installation regardless of the currently installed package version. + * + * @hide + */ + public void setRequiredInstalledVersionCode(long versionCode) { + requiredInstalledVersionCode = versionCode; + } + /** {@hide} */ public void setInstallFlagsForcePermissionPrompt() { installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT; @@ -1708,6 +1720,7 @@ public class PackageInstaller { pw.printPair("installerPackageName", installerPackageName); pw.printPair("isMultiPackage", isMultiPackage); pw.printPair("isStaged", isStaged); + pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode); pw.println(); } @@ -1736,6 +1749,7 @@ public class PackageInstaller { dest.writeString(installerPackageName); dest.writeBoolean(isMultiPackage); dest.writeBoolean(isStaged); + dest.writeLong(requiredInstalledVersionCode); } public static final Parcelable.Creator<SessionParams> diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 33c0bb9dc9d0..dd5ca6706316 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1423,6 +1423,14 @@ public abstract class PackageManager { */ public static final int INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY = -120; + /** + * Installation failed return code: the required installed version code + * does not match the currently installed package version code. + * + * @hide + */ + public static final int INSTALL_FAILED_WRONG_INSTALLED_VERSION = -121; + /** @hide */ @IntDef(flag = true, prefix = { "DELETE_" }, value = { DELETE_KEEP_DATA, @@ -6918,6 +6926,7 @@ public abstract class PackageManager { case INSTALL_FAILED_BAD_DEX_METADATA: return "INSTALL_FAILED_BAD_DEX_METADATA"; case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT"; case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE"; + case INSTALL_FAILED_WRONG_INSTALLED_VERSION: return "INSTALL_FAILED_WRONG_INSTALLED_VERSION"; default: return Integer.toString(status); } } diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java index b44458a7d449..cc5d3b16ff08 100644 --- a/core/java/android/database/AbstractCursor.java +++ b/core/java/android/database/AbstractCursor.java @@ -419,26 +419,36 @@ public abstract class AbstractCursor implements CrossProcessCursor { Preconditions.checkNotNull(cr); Preconditions.checkNotNull(notifyUris); - setNotificationUris(cr, notifyUris, cr.getUserId()); + setNotificationUris(cr, notifyUris, cr.getUserId(), true); } - /** @hide - set the notification uri but with an observer for a particular user's view */ - public void setNotificationUris(ContentResolver cr, List<Uri> notifyUris, int userHandle) { + /** + * Set the notification uri but with an observer for a particular user's view. Also allows + * disabling the use of a self observer, which is sensible if either + * a) the cursor's owner calls {@link #onChange(boolean)} whenever the content changes, or + * b) the cursor is known not to have any content observers. + * @hide + */ + public void setNotificationUris(ContentResolver cr, List<Uri> notifyUris, int userHandle, + boolean registerSelfObserver) { synchronized (mSelfObserverLock) { mNotifyUris = notifyUris; mNotifyUri = mNotifyUris.get(0); mContentResolver = cr; if (mSelfObserver != null) { mContentResolver.unregisterContentObserver(mSelfObserver); + mSelfObserverRegistered = false; } - mSelfObserver = new SelfContentObserver(this); - final int size = mNotifyUris.size(); - for (int i = 0; i < size; ++i) { - final Uri notifyUri = mNotifyUris.get(i); - mContentResolver.registerContentObserver( - notifyUri, true, mSelfObserver, userHandle); + if (registerSelfObserver) { + mSelfObserver = new SelfContentObserver(this); + final int size = mNotifyUris.size(); + for (int i = 0; i < size; ++i) { + final Uri notifyUri = mNotifyUris.get(i); + mContentResolver.registerContentObserver( + notifyUri, true, mSelfObserver, userHandle); + } + mSelfObserverRegistered = true; } - mSelfObserverRegistered = true; } } diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 4aa6fabe8989..099ae291c8f4 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -364,6 +364,8 @@ public final class Sensor { * count steps if it is not activated. This sensor is ideal for fitness tracking applications. * It is defined as an {@link Sensor#REPORTING_MODE_ON_CHANGE} sensor. * <p> + * This sensor requires permission {@code android.permission.ACTIVITY_RECOGNITION}. + * <p> * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details. */ public static final int TYPE_STEP_COUNTER = 19; @@ -382,6 +384,8 @@ public final class Sensor { * gyroscope. This sensor uses lower power than the other rotation vectors, because it doesn't * use the gyroscope. However, it is more noisy and will work best outdoors. * <p> + * This sensor requires permission {@code android.permission.ACTIVITY_RECOGNITION}. + * <p> * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details. */ public static final int TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20; diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index d7db1f5f8b13..226b8e549a9e 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -36,6 +36,7 @@ import static com.android.internal.util.Preconditions.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; /** * A class for describing camera output, which contains a {@link Surface} and its specific @@ -692,7 +693,8 @@ public final class OutputConfiguration implements Parcelable { mIsShared != other.mIsShared || mConfiguredFormat != other.mConfiguredFormat || mConfiguredDataspace != other.mConfiguredDataspace || - mConfiguredGenerationId != other.mConfiguredGenerationId) + mConfiguredGenerationId != other.mConfiguredGenerationId || + !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId)) return false; int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size()); diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java index 83e19803a63b..3cdebac413f9 100644 --- a/core/java/android/os/BugreportManager.java +++ b/core/java/android/os/BugreportManager.java @@ -26,12 +26,14 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; +import android.util.Log; import com.android.internal.util.Preconditions; import libcore.io.IoUtils; -import java.io.FileDescriptor; +import java.io.File; +import java.io.FileNotFoundException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.Executor; @@ -45,6 +47,9 @@ import java.util.concurrent.Executor; @TestApi @SystemService(Context.BUGREPORT_SERVICE) public final class BugreportManager { + + private static final String TAG = "BugreportManager"; + private final Context mContext; private final IDumpstate mBinder; @@ -149,16 +154,22 @@ public final class BugreportManager { Preconditions.checkNotNull(executor); Preconditions.checkNotNull(callback); + if (screenshotFd == null) { + // Binder needs a valid File Descriptor to be passed + screenshotFd = ParcelFileDescriptor.open(new File("/dev/null"), + ParcelFileDescriptor.MODE_READ_ONLY); + } DumpstateListener dsListener = new DumpstateListener(executor, callback); // Note: mBinder can get callingUid from the binder transaction. mBinder.startBugreport(-1 /* callingUid */, mContext.getOpPackageName(), bugreportFd.getFileDescriptor(), - (screenshotFd != null - ? screenshotFd.getFileDescriptor() : new FileDescriptor()), + screenshotFd.getFileDescriptor(), params.getMode(), dsListener); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); + } catch (FileNotFoundException e) { + Log.wtf(TAG, "Not able to find /dev/null file: ", e); } finally { // We can close the file descriptors here because binder would have duped them. IoUtils.closeQuietly(bugreportFd); diff --git a/core/java/android/os/IncidentManager.java b/core/java/android/os/IncidentManager.java index 08afe31f05dd..1f61a3c47ff1 100644 --- a/core/java/android/os/IncidentManager.java +++ b/core/java/android/os/IncidentManager.java @@ -519,6 +519,13 @@ public class IncidentManager { android.Manifest.permission.PACKAGE_USAGE_STATS }) public @Nullable IncidentReport getIncidentReport(Uri uri) { + final String id = uri.getQueryParameter(URI_PARAM_REPORT_ID); + if (id == null) { + // If there's no report id, it's a bug report, so we can't return the incident + // report. + return null; + } + final String pkg = uri.getQueryParameter(URI_PARAM_CALLING_PACKAGE); if (pkg == null) { throw new RuntimeException("Invalid URI: No " @@ -531,13 +538,6 @@ public class IncidentManager { + URI_PARAM_RECEIVER_CLASS + " parameter. " + uri); } - final String id = uri.getQueryParameter(URI_PARAM_REPORT_ID); - if (cls == null) { - // If there's no report id, it's a bug report, so we can't return the incident - // report. - return null; - } - try { return getCompanionServiceLocked().getIncidentReport(pkg, cls, id); } catch (RemoteException ex) { diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 7cc7ccd2ad16..1b41694e7d48 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -866,8 +866,8 @@ public class RecoverySystem { if (storageManager.needsCheckpoint()) { Log.i(TAG, "Rescue Party requested wipe. Aborting update instead."); storageManager.abortChanges("rescueparty", false); + return; } - return; } catch (RemoteException e) { Log.i(TAG, "Failed to handle with checkpointing. Continuing with wipe."); } diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 6478de2612c3..85361587119d 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -881,6 +881,7 @@ public class ZygoteProcess { maybeSetApiBlacklistExemptions(primaryZygoteState, false); maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState); + maybeSetHiddenApiAccessStatslogSampleRate(primaryZygoteState); } } @@ -896,6 +897,7 @@ public class ZygoteProcess { maybeSetApiBlacklistExemptions(secondaryZygoteState, false); maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState); + maybeSetHiddenApiAccessStatslogSampleRate(secondaryZygoteState); } } diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 774d4ae789b4..9a11104b30c1 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -263,6 +263,7 @@ public final class DeviceConfig { * Namespace for TextClassifier related features. * * @hide + * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS */ @SystemApi public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier"; diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 4632b7553ca0..4ac485099da8 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -36,6 +36,7 @@ import android.graphics.Bitmap; import android.graphics.ImageDecoder; import android.graphics.Point; import android.media.ExifInterface; +import android.media.MediaFile; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -1698,6 +1699,18 @@ public final class DocumentsContract { } catch (IOException e) { } + // Use ImageDecoder to do full image decode of heif format file + // will have right orientation. So, we don't need to add orientation + // information into extras. + final String mimeType = MediaFile.getMimeTypeForFile(file.getName()); + if (mimeType.equals("image/heif") + || mimeType.equals("image/heif-sequence") + || mimeType.equals("image/heic") + || mimeType.equals("image/heic-sequence")) { + return new AssetFileDescriptor(pfd, 0 /* startOffset */, + AssetFileDescriptor.UNKNOWN_LENGTH, null /* extras */); + } + return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b0e980e7206f..75b40fd489f2 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11866,6 +11866,7 @@ public final class Settings { * sync_adapter_duration (long) * exempted_sync_duration (long) * system_interaction_duration (long) + * initial_foreground_service_start_duration (long) * stable_charging_threshold (long) * * idle_duration (long) // This is deprecated and used to circumvent b/26355386. @@ -12038,26 +12039,27 @@ public final class Settings { * entity_list_default use ":" as delimiter for values. Ex: * * <pre> - * smart_linkify_enabled (boolean) - * system_textclassifier_enabled (boolean) - * model_dark_launch_enabled (boolean) - * smart_selection_enabled (boolean) - * smart_text_share_enabled (boolean) - * smart_linkify_enabled (boolean) - * smart_select_animation_enabled (boolean) - * suggest_selection_max_range_length (int) * classify_text_max_range_length (int) - * generate_links_max_text_length (int) - * generate_links_log_sample_rate (int) + * detect_language_from_text_enabled (boolean) * entity_list_default (String[]) - * entity_list_not_editable (String[]) * entity_list_editable (String[]) + * entity_list_not_editable (String[]) + * generate_links_log_sample_rate (int) + * generate_links_max_text_length (int) * in_app_conversation_action_types_default (String[]) - * notification_conversation_action_types_default (String[]) + * lang_id_context_settings (float[]) * lang_id_threshold_override (float) + * local_textclassifier_enabled (boolean) + * model_dark_launch_enabled (boolean) + * notification_conversation_action_types_default (String[]) + * smart_linkify_enabled (boolean) + * smart_select_animation_enabled (boolean) + * smart_selection_enabled (boolean) + * smart_text_share_enabled (boolean) + * suggest_selection_max_range_length (int) + * system_textclassifier_enabled (boolean) * template_intent_factory_enabled (boolean) * translate_in_classification_enabled (boolean) - * detect_language_from_text_enabled (boolean) * </pre> * * <p> diff --git a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java index 55e61410b9e2..516d33673012 100644 --- a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java +++ b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java @@ -30,6 +30,7 @@ import android.app.contentsuggestions.ISelectionsCallback; import android.app.contentsuggestions.SelectionsRequest; import android.content.Intent; import android.graphics.Bitmap; +import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.os.Bundle; import android.os.Handler; @@ -62,11 +63,15 @@ public abstract class ContentSuggestionsService extends Service { private final IContentSuggestionsService mInterface = new IContentSuggestionsService.Stub() { @Override public void provideContextImage(int taskId, GraphicBuffer contextImage, - Bundle imageContextRequestExtras) { + int colorSpaceId, Bundle imageContextRequestExtras) { Bitmap wrappedBuffer = null; if (contextImage != null) { - wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, null); + ColorSpace colorSpace = null; + if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) { + colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]); + } + wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace); } mHandler.sendMessage( diff --git a/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl b/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl index 1926478322c8..6240e00794bd 100644 --- a/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl +++ b/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl @@ -32,6 +32,7 @@ oneway interface IContentSuggestionsService { void provideContextImage( int taskId, in GraphicBuffer contextImage, + int colorSpaceId, in Bundle imageContextRequestExtras); void suggestContentSelections( in SelectionsRequest request, diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index cd8c7aea1bab..c9d37bf86a27 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -1995,7 +1995,8 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall } /** - * @return the index of the activity that this state is for. + * @return the index of the activity that this state is for or -1 + * if there was no assist data captured. */ public @IntRange(from = -1) int getIndex() { return mIndex; @@ -2048,7 +2049,8 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall } /** - * Represents the id of an assist source activity. + * Represents the id of an assist source activity. You can use + * {@link #equals(Object)} to compare instances of this class. */ public static class ActivityId { private final int mTaskId; diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index e8b0d92c2fd2..ae36e4ecde17 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -36,7 +36,6 @@ public class FeatureFlagUtils { public static final String PERSIST_PREFIX = "persist." + FFLAG_OVERRIDE_PREFIX; public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer"; public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid"; - public static final String SAFETY_HUB = "settings_safety_hub"; public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press"; public static final String FORCE_GLOBAL_ACTIONS_GRID_ENABLED = "settings_global_actions_force_grid_enabled"; @@ -57,7 +56,6 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false"); DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false"); DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false"); - DEFAULT_FLAGS.put(SAFETY_HUB, "true"); DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false"); DEFAULT_FLAGS.put(FORCE_GLOBAL_ACTIONS_GRID_ENABLED, "false"); DEFAULT_FLAGS.put(GLOBAL_ACTIONS_PANEL_ENABLED, "true"); diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index c730fe2dc114..b347a78a8780 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -621,18 +621,6 @@ interface IWindowManager */ void setShouldShowIme(int displayId, boolean shouldShow); - /** - * Reparent the top layers for a display to the requested surfaceControl. The display that - * is going to be re-parented (the displayId passed in) needs to have been created by the same - * process that is requesting the re-parent. This is to ensure clients can't just re-parent - * display content info to any SurfaceControl, as this would be a security issue. - * - * @param displayId The id of the display. - * @param surfaceControlHandle The SurfaceControl that the top level layers for the - * display should be re-parented to. - */ - void reparentDisplayContent(int displayId, in SurfaceControl sc); - /** * Waits for transactions to get applied before injecting input. * This includes waiting for the input windows to get sent to InputManager. diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index b52fdb8399d1..d269323d50a4 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -257,6 +257,31 @@ interface IWindowSession { void updatePointerIcon(IWindow window); /** + * Reparent the top layers for a display to the requested SurfaceControl. The display that is + * going to be re-parented (the displayId passed in) needs to have been created by the same + * process that is requesting the re-parent. This is to ensure clients can't just re-parent + * display content info to any SurfaceControl, as this would be a security issue. + * + * @param window The window which owns the SurfaceControl. This indicates the z-order of the + * windows of this display against the windows on the parent display. + * @param sc The SurfaceControl that the top level layers for the display should be re-parented + * to. + * @param displayId The id of the display to be re-parented. + */ + void reparentDisplayContent(IWindow window, in SurfaceControl sc, int displayId); + + /** + * Update the location of a child display in its parent window. This enables windows in the + * child display to compute the global transformation matrix. + * + * @param window The parent window of the display. + * @param x The x coordinate in the parent window. + * @param y The y coordinate in the parent window. + * @param displayId The id of the display to be notified. + */ + void updateDisplayContentLocation(IWindow window, int x, int y, int displayId); + + /** * Update a tap exclude region identified by provided id in the window. Touches on this region * will neither be dispatched to this window nor change the focus to this window. Passing an * invalid region will remove the area from the exclude region of this window. diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java index d415387808dd..83abf1a33b2d 100644 --- a/core/java/android/view/ImeInsetsSourceConsumer.java +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -173,6 +173,6 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { } private InputMethodManager getImm() { - return mController.getViewRoot().mDisplayContext.getSystemService(InputMethodManager.class); + return mController.getViewRoot().mContext.getSystemService(InputMethodManager.class); } } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 8070e76d1b40..bbb90536e8f8 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -848,6 +848,10 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb position.width() / (float) mSurfaceWidth, 0.0f, 0.0f, position.height() / (float) mSurfaceHeight); + if (mViewVisibility) { + mRtTransaction.show(surface); + } + } private void setParentSpaceRectangle(Rect position, long frameNumber) { @@ -914,27 +918,15 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb if (mSurfaceControl == null) { return; } - if (mRtHandlingPositionUpdates) { - mRtHandlingPositionUpdates = false; - // This callback will happen while the UI thread is blocked, so we can - // safely access other member variables at this time. - // So do what the UI thread would have done if RT wasn't handling position - // updates. - if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) { - try { - if (DEBUG) { - Log.d(TAG, String.format("%d updateSurfacePosition, " - + "postion = [%d, %d, %d, %d]", - System.identityHashCode(this), - mScreenRect.left, mScreenRect.top, - mScreenRect.right, mScreenRect.bottom)); - } - setParentSpaceRectangle(mScreenRect, frameNumber); - } catch (Exception ex) { - Log.e(TAG, "Exception configuring surface", ex); - } - } + + if (frameNumber > 0) { + final ViewRootImpl viewRoot = getViewRootImpl(); + + mRtTransaction.deferTransactionUntilSurface(mSurfaceControl, viewRoot.mSurface, + frameNumber); } + mRtTransaction.hide(mSurfaceControl); + mRtTransaction.apply(); } }; diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 3bae4b855374..3d3d5dc7db32 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -177,7 +177,7 @@ public final class ThreadedRenderer extends HardwareRenderer { * Forces smart-dark to be always on. * @hide */ - public static final String DEBUG_FORCE_DARK = "persist.hwui.force_dark"; + public static final String DEBUG_FORCE_DARK = "debug.hwui.force_dark"; public static int EGL_CONTEXT_PRIORITY_HIGH_IMG = 0x3101; public static int EGL_CONTEXT_PRIORITY_MEDIUM_IMG = 0x3102; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 5929c1b4d6e6..921294a78e5e 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -7054,10 +7054,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private FrameMetricsObserver findFrameMetricsObserver( Window.OnFrameMetricsAvailableListener listener) { - for (int i = 0; i < mFrameMetricsObservers.size(); i++) { - FrameMetricsObserver observer = mFrameMetricsObservers.get(i); - if (observer.mListener == listener) { - return observer; + if (mFrameMetricsObservers != null) { + for (int i = 0; i < mFrameMetricsObservers.size(); i++) { + FrameMetricsObserver observer = mFrameMetricsObservers.get(i); + if (observer.mListener == listener) { + return observer; + } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index b813bc34c763..4463e13ca5ee 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -283,13 +283,7 @@ public final class ViewRootImpl implements ViewParent, @GuardedBy("mWindowCallbacks") final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList<>(); @UnsupportedAppUsage - final Context mContext; - /** - * TODO(b/116349163): Check if we can merge this into {@link #mContext}. - * @hide - */ - @NonNull - public Context mDisplayContext; + public final Context mContext; @UnsupportedAppUsage final IWindowSession mWindowSession; @@ -595,7 +589,6 @@ public final class ViewRootImpl implements ViewParent, public ViewRootImpl(Context context, Display display) { mContext = context; - mDisplayContext = context.createDisplayContext(display); mWindowSession = WindowManagerGlobal.getWindowSession(); mDisplay = display; mBasePackageName = context.getBasePackageName(); @@ -1379,7 +1372,7 @@ public final class ViewRootImpl implements ViewParent, } else { mDisplay = preferredDisplay; } - mDisplayContext = mContext.createDisplayContext(mDisplay); + mContext.updateDisplay(mDisplay.getDisplayId()); } void pokeDrawLockIfNeeded() { @@ -2725,7 +2718,7 @@ public final class ViewRootImpl implements ViewParent, .mayUseInputMethod(mWindowAttributes.flags); if (imTarget != mLastWasImTarget) { mLastWasImTarget = imTarget; - InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class); + InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); if (imm != null && imTarget) { imm.onPreWindowFocus(mView, hasWindowFocus); imm.onPostWindowFocus(mView, mView.findFocus(), @@ -2859,7 +2852,7 @@ public final class ViewRootImpl implements ViewParent, mLastWasImTarget = WindowManager.LayoutParams .mayUseInputMethod(mWindowAttributes.flags); - InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class); + InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) { imm.onPreWindowFocus(mView, hasWindowFocus); } @@ -4564,8 +4557,7 @@ public final class ViewRootImpl implements ViewParent, enqueueInputEvent(event, null, 0, true); } break; case MSG_CHECK_FOCUS: { - InputMethodManager imm = - mDisplayContext.getSystemService(InputMethodManager.class); + InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); if (imm != null) { imm.checkFocus(); } @@ -5110,7 +5102,7 @@ public final class ViewRootImpl implements ViewParent, @Override protected int onProcess(QueuedInputEvent q) { if (mLastWasImTarget && !isInLocalFocusMode()) { - InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class); + InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); if (imm != null) { final InputEvent event = q.mEvent; if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event); diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java index 2c7929939053..b387a68dd8a3 100644 --- a/core/java/android/view/autofill/AutofillId.java +++ b/core/java/android/view/autofill/AutofillId.java @@ -16,6 +16,7 @@ package android.view.autofill; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -34,10 +35,10 @@ public final class AutofillId implements Parcelable { private static final int FLAG_HAS_SESSION = 0x4; private final int mViewId; - private final int mFlags; + private int mFlags; private final int mVirtualIntId; private final long mVirtualLongId; - private final int mSessionId; + private int mSessionId; /** @hide */ @TestApi @@ -72,6 +73,12 @@ public final class AutofillId implements Parcelable { } /** @hide */ + public static AutofillId withoutSession(@NonNull AutofillId id) { + final int flags = id.mFlags & ~FLAG_HAS_SESSION; + return new AutofillId(flags, id.mViewId, id.mVirtualLongId, NO_SESSION); + } + + /** @hide */ public int getViewId() { return mViewId; } @@ -135,7 +142,8 @@ public final class AutofillId implements Parcelable { return !isVirtualInt() && !isVirtualLong(); } - private boolean hasSession() { + /** @hide */ + public boolean hasSession() { return (mFlags & FLAG_HAS_SESSION) != 0; } @@ -144,6 +152,18 @@ public final class AutofillId implements Parcelable { return mSessionId; } + /** @hide */ + public void setSessionId(int sessionId) { + mFlags |= FLAG_HAS_SESSION; + mSessionId = sessionId; + } + + /** @hide */ + public void resetSessionId() { + mFlags &= ~FLAG_HAS_SESSION; + mSessionId = NO_SESSION; + } + ///////////////////////////////// // Object "contract" methods. // ///////////////////////////////// @@ -172,6 +192,17 @@ public final class AutofillId implements Parcelable { return true; } + /** @hide */ + @TestApi + public boolean equalsIgnoreSession(@Nullable AutofillId other) { + if (this == other) return true; + if (other == null) return false; + if (mViewId != other.mViewId) return false; + if (mVirtualIntId != other.mVirtualIntId) return false; + if (mVirtualLongId != other.mVirtualLongId) return false; + return true; + } + @Override public String toString() { final StringBuilder builder = new StringBuilder().append(mViewId); diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 6503a800acb7..5872d3f7f785 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -1139,6 +1139,7 @@ public final class AutofillManager { if (mEnteredIds == null) { mEnteredIds = new ArraySet<>(1); } + id.resetSessionId(); mEnteredIds.add(id); } @@ -2177,6 +2178,9 @@ public final class AutofillManager { private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds, boolean saveOnAllViewsInvisible, boolean saveOnFinish, @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) { + if (saveTriggerId != null) { + saveTriggerId.resetSessionId(); + } synchronized (mLock) { if (sVerbose) { Log.v(TAG, "setTrackedViews(): sessionId=" + sessionId @@ -2202,6 +2206,7 @@ public final class AutofillManager { mFillableIds = new ArraySet<>(fillableIds.length); } for (AutofillId id : fillableIds) { + id.resetSessionId(); mFillableIds.add(id); } } @@ -2264,6 +2269,11 @@ public final class AutofillManager { * session when they're entered. */ private void setSessionFinished(int newState, @Nullable List<AutofillId> autofillableIds) { + if (autofillableIds != null) { + for (int i = 0; i < autofillableIds.size(); i++) { + autofillableIds.get(i).resetSessionId(); + } + } synchronized (mLock) { if (sVerbose) { Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to " @@ -2879,6 +2889,7 @@ public final class AutofillManager { final int numIds = trackedIds.length; for (int i = 0; i < numIds; i++) { final AutofillId id = trackedIds[i]; + id.resetSessionId(); if (isVisible[i]) { mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id); diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index c2ad82fd5c5f..e3c2bd1b2a26 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -37,6 +37,7 @@ import android.os.ServiceManager; import android.util.Log; import android.view.View; import android.view.ViewStructure; +import android.view.WindowManager; import android.view.contentcapture.ContentCaptureSession.FlushReason; import com.android.internal.annotations.GuardedBy; @@ -343,10 +344,9 @@ public final class ContentCaptureManager { /** @hide */ @UiThread public void onActivityCreated(@NonNull IBinder applicationToken, - @NonNull ComponentName activityComponent, int flags) { + @NonNull ComponentName activityComponent) { if (mOptions.lite) return; synchronized (mLock) { - mFlags |= flags; getMainContentCaptureSession().start(applicationToken, activityComponent, mFlags); } } @@ -484,8 +484,43 @@ public final class ContentCaptureManager { Log.d(TAG, "setContentCaptureEnabled(): setting to " + enabled + " for " + mContext); } + MainContentCaptureSession mainSession; synchronized (mLock) { - mFlags |= enabled ? 0 : ContentCaptureContext.FLAG_DISABLED_BY_APP; + if (enabled) { + mFlags &= ~ContentCaptureContext.FLAG_DISABLED_BY_APP; + } else { + mFlags |= ContentCaptureContext.FLAG_DISABLED_BY_APP; + } + mainSession = mMainSession; + } + if (mainSession != null) { + mainSession.setDisabled(!enabled); + } + } + + /** + * Called by apps to update flag secure when window attributes change. + * + * @hide + */ + public void updateWindowAttributes(@NonNull WindowManager.LayoutParams params) { + if (sDebug) { + Log.d(TAG, "updateWindowAttributes(): window flags=" + params.flags); + } + final boolean flagSecureEnabled = + (params.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0; + + MainContentCaptureSession mainSession; + synchronized (mLock) { + if (flagSecureEnabled) { + mFlags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE; + } else { + mFlags &= ~ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE; + } + mainSession = mMainSession; + } + if (mainSession != null) { + mainSession.setDisabled(flagSecureEnabled); } } diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index 784cf9c32557..7241664602e9 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -28,6 +28,7 @@ import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APP import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString; import static android.view.contentcapture.ContentCaptureHelper.sDebug; import static android.view.contentcapture.ContentCaptureHelper.sVerbose; +import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE; import android.annotation.NonNull; import android.annotation.Nullable; @@ -80,6 +81,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession { */ public static final String EXTRA_BINDER = "binder"; + /** + * Name of the {@link IResultReceiver} extra used to pass the content capture enabled state. + * @hide + */ + public static final String EXTRA_ENABLED_STATE = "enabled"; + @NonNull private final AtomicBoolean mDisabled = new AtomicBoolean(false); @@ -155,6 +162,13 @@ public final class MainContentCaptureSession extends ContentCaptureSession { public void send(int resultCode, Bundle resultData) { final IBinder binder; if (resultData != null) { + // Change in content capture enabled. + final boolean hasEnabled = resultData.getBoolean(EXTRA_ENABLED_STATE); + if (hasEnabled) { + final boolean disabled = (resultCode == RESULT_CODE_FALSE); + mDisabled.set(disabled); + return; + } binder = resultData.getBinder(EXTRA_BINDER); if (binder == null) { Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result"); @@ -578,6 +592,15 @@ public final class MainContentCaptureSession extends ContentCaptureSession { return mDisabled.get(); } + /** + * Sets the disabled state of content capture. + * + * @return whether disabled state was changed. + */ + boolean setDisabled(boolean disabled) { + return mDisabled.compareAndSet(!disabled, disabled); + } + // TODO(b/122454205): refactor "notifyXXXX" methods below to a common "Buffer" object that is // shared between ActivityContentCaptureSession and ChildContentCaptureSession objects. Such // change should also get get rid of the "internalNotifyXXXX" methods above diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java index ee5b3ecc7682..21ead4c62366 100644 --- a/core/java/android/view/inputmethod/CursorAnchorInfo.java +++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java @@ -17,6 +17,7 @@ package android.view.inputmethod; import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.Matrix; import android.graphics.RectF; import android.os.Parcel; @@ -388,7 +389,7 @@ public final class CursorAnchorInfo implements Parcelable { "required when positional parameters are specified."); } } - return new CursorAnchorInfo(this); + return CursorAnchorInfo.create(this); } /** @@ -412,30 +413,90 @@ public final class CursorAnchorInfo implements Parcelable { } } - private CursorAnchorInfo(final Builder builder) { - mSelectionStart = builder.mSelectionStart; - mSelectionEnd = builder.mSelectionEnd; - mComposingTextStart = builder.mComposingTextStart; - mComposingText = builder.mComposingText; - mInsertionMarkerFlags = builder.mInsertionMarkerFlags; - mInsertionMarkerHorizontal = builder.mInsertionMarkerHorizontal; - mInsertionMarkerTop = builder.mInsertionMarkerTop; - mInsertionMarkerBaseline = builder.mInsertionMarkerBaseline; - mInsertionMarkerBottom = builder.mInsertionMarkerBottom; - mCharacterBoundsArray = builder.mCharacterBoundsArrayBuilder != null ? - builder.mCharacterBoundsArrayBuilder.build() : null; - mMatrixValues = new float[9]; + private static CursorAnchorInfo create(Builder builder) { + final SparseRectFArray characterBoundsArray = + builder.mCharacterBoundsArrayBuilder != null + ? builder.mCharacterBoundsArrayBuilder.build() + : null; + final float[] matrixValues = new float[9]; if (builder.mMatrixInitialized) { - System.arraycopy(builder.mMatrixValues, 0, mMatrixValues, 0, 9); + System.arraycopy(builder.mMatrixValues, 0, matrixValues, 0, 9); } else { - Matrix.IDENTITY_MATRIX.getValues(mMatrixValues); + Matrix.IDENTITY_MATRIX.getValues(matrixValues); } + return new CursorAnchorInfo(builder.mSelectionStart, builder.mSelectionEnd, + builder.mComposingTextStart, builder.mComposingText, builder.mInsertionMarkerFlags, + builder.mInsertionMarkerHorizontal, builder.mInsertionMarkerTop, + builder.mInsertionMarkerBaseline, builder.mInsertionMarkerBottom, + characterBoundsArray, matrixValues); + } + + private CursorAnchorInfo(int selectionStart, int selectionEnd, int composingTextStart, + @Nullable CharSequence composingText, int insertionMarkerFlags, + float insertionMarkerHorizontal, float insertionMarkerTop, + float insertionMarkerBaseline, float insertionMarkerBottom, + @Nullable SparseRectFArray characterBoundsArray, @NonNull float[] matrixValues) { + mSelectionStart = selectionStart; + mSelectionEnd = selectionEnd; + mComposingTextStart = composingTextStart; + mComposingText = composingText; + mInsertionMarkerFlags = insertionMarkerFlags; + mInsertionMarkerHorizontal = insertionMarkerHorizontal; + mInsertionMarkerTop = insertionMarkerTop; + mInsertionMarkerBaseline = insertionMarkerBaseline; + mInsertionMarkerBottom = insertionMarkerBottom; + mCharacterBoundsArray = characterBoundsArray; + mMatrixValues = matrixValues; + // To keep hash function simple, we only use some complex objects for hash. - int hash = Objects.hashCode(mComposingText); - hash *= 31; - hash += Arrays.hashCode(mMatrixValues); - mHashCode = hash; + int hashCode = Objects.hashCode(mComposingText); + hashCode *= 31; + hashCode += Arrays.hashCode(matrixValues); + mHashCode = hashCode; + } + + /** + * Creates a new instance of {@link CursorAnchorInfo} by applying {@code parentMatrix} to + * the coordinate transformation matrix. + * + * @param original {@link CursorAnchorInfo} to be cloned from. + * @param parentMatrix {@link Matrix} to be applied to {@code original.getMatrix()} + * @return A new instance of {@link CursorAnchorInfo} whose {@link CursorAnchorInfo#getMatrix()} + * returns {@code parentMatrix * original.getMatrix()}. + * @hide + */ + public static CursorAnchorInfo createForAdditionalParentMatrix(CursorAnchorInfo original, + @NonNull Matrix parentMatrix) { + return new CursorAnchorInfo(original.mSelectionStart, original.mSelectionEnd, + original.mComposingTextStart, original.mComposingText, + original.mInsertionMarkerFlags, original.mInsertionMarkerHorizontal, + original.mInsertionMarkerTop, original.mInsertionMarkerBaseline, + original.mInsertionMarkerBottom, original.mCharacterBoundsArray, + computeMatrixValues(parentMatrix, original)); + } + + /** + * Returns a float array that represents {@link Matrix} elements for + * {@code parentMatrix * info.getMatrix()}. + * + * @param parentMatrix {@link Matrix} to be multiplied. + * @param info {@link CursorAnchorInfo} to provide {@link Matrix} to be multiplied. + * @return {@code parentMatrix * info.getMatrix()}. + */ + private static float[] computeMatrixValues(@NonNull Matrix parentMatrix, + @NonNull CursorAnchorInfo info) { + if (parentMatrix.isIdentity()) { + return info.mMatrixValues; + } + + final Matrix newMatrix = new Matrix(); + newMatrix.setValues(info.mMatrixValues); + newMatrix.postConcat(parentMatrix); + + final float[] matrixValues = new float[9]; + newMatrix.getValues(matrixValues); + return matrixValues; } /** diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index fd73856bf79b..c7b1cdab4361 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -510,7 +510,7 @@ public final class InputMethodManager { return null; } final InputMethodManager fallbackImm = - viewRootImpl.mDisplayContext.getSystemService(InputMethodManager.class); + viewRootImpl.mContext.getSystemService(InputMethodManager.class); if (fallbackImm == null) { Log.e(TAG, "b/117267690: Failed to get non-null fallback IMM. view=" + view); return null; @@ -2887,6 +2887,16 @@ public final class InputMethodManager { } } + /** + * <p>This is used for CTS test only. Do not use this method outside of CTS package.<p/> + * @return the ID of this display which this {@link InputMethodManager} resides + * @hide + */ + @TestApi + public int getDisplayId() { + return mDisplayId; + } + void doDump(FileDescriptor fd, PrintWriter fout, String[] args) { final Printer p = new PrintWriterPrinter(fout); p.println("Input method client state for " + this + ":"); diff --git a/core/java/android/view/textclassifier/ConfigParser.java b/core/java/android/view/textclassifier/ConfigParser.java index b475412bf669..63de0590ef72 100644 --- a/core/java/android/view/textclassifier/ConfigParser.java +++ b/core/java/android/view/textclassifier/ConfigParser.java @@ -17,9 +17,19 @@ package android.view.textclassifier; import android.annotation.Nullable; import android.provider.DeviceConfig; +import android.util.ArrayMap; import android.util.KeyValueListParser; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.internal.util.Preconditions; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; /** * Retrieves settings from {@link DeviceConfig} and {@link android.provider.Settings}. @@ -27,80 +37,228 @@ import com.android.internal.annotations.VisibleForTesting; * * @hide */ -@VisibleForTesting +@VisibleForTesting(visibility = Visibility.PACKAGE) public final class ConfigParser { private static final String TAG = "ConfigParser"; - private final KeyValueListParser mParser; + static final boolean ENABLE_DEVICE_CONFIG = true; + + private static final String STRING_LIST_DELIMITER = ":"; - // TODO: Re-enable DeviceConfig when it has reasonable performance or just delete the - // option of using DeviceConfig entirely. - static final boolean ENABLE_DEVICE_CONFIG = false; + private final Supplier<String> mLegacySettingsSupplier; + private final Object mLock = new Object(); + @GuardedBy("mLock") + private final Map<String, Object> mCache = new ArrayMap<>(); + @GuardedBy("mLock") + private @Nullable KeyValueListParser mSettingsParser; // Call getLegacySettings() instead. + + public ConfigParser(Supplier<String> legacySettingsSupplier) { + mLegacySettingsSupplier = Preconditions.checkNotNull(legacySettingsSupplier); + } - public ConfigParser(@Nullable String textClassifierConstants) { - final KeyValueListParser parser = new KeyValueListParser(','); - try { - parser.setString(textClassifierConstants); - } catch (IllegalArgumentException e) { - // Failed to parse the settings string, log this and move on with defaults. - Log.w(TAG, "Bad text_classifier_constants: " + textClassifierConstants); + private KeyValueListParser getLegacySettings() { + synchronized (mLock) { + if (mSettingsParser == null) { + final String legacySettings = mLegacySettingsSupplier.get(); + try { + mSettingsParser = new KeyValueListParser(','); + mSettingsParser.setString(legacySettings); + } catch (IllegalArgumentException e) { + // Failed to parse the settings string, log this and move on with defaults. + Log.w(TAG, "Bad text_classifier_constants: " + legacySettings); + } + } + return mSettingsParser; } - mParser = parser; } /** - * Reads a boolean flag. + * Reads a boolean setting through the cache. */ public boolean getBoolean(String key, boolean defaultValue) { - if (ENABLE_DEVICE_CONFIG) { - return DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_TEXTCLASSIFIER, - key, - mParser.getBoolean(key, defaultValue)); - } else { - return mParser.getBoolean(key, defaultValue); + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof Boolean) { + return (boolean) cached; + } + final boolean value; + if (ENABLE_DEVICE_CONFIG) { + value = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_TEXTCLASSIFIER, + key, + getLegacySettings().getBoolean(key, defaultValue)); + } else { + value = getLegacySettings().getBoolean(key, defaultValue); + } + mCache.put(key, value); + return value; } } /** - * Reads an integer flag. + * Reads an integer setting through the cache. */ public int getInt(String key, int defaultValue) { - if (ENABLE_DEVICE_CONFIG) { - return DeviceConfig.getInt( - DeviceConfig.NAMESPACE_TEXTCLASSIFIER, - key, - mParser.getInt(key, defaultValue)); - } else { - return mParser.getInt(key, defaultValue); + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof Integer) { + return (int) cached; + } + final int value; + if (ENABLE_DEVICE_CONFIG) { + value = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_TEXTCLASSIFIER, + key, + getLegacySettings().getInt(key, defaultValue)); + } else { + value = getLegacySettings().getInt(key, defaultValue); + } + mCache.put(key, value); + return value; } } /** - * Reads a float flag. + * Reads a float setting through the cache. */ public float getFloat(String key, float defaultValue) { - if (ENABLE_DEVICE_CONFIG) { - return DeviceConfig.getFloat( - DeviceConfig.NAMESPACE_TEXTCLASSIFIER, - key, - mParser.getFloat(key, defaultValue)); - } else { - return mParser.getFloat(key, defaultValue); + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof Float) { + return (float) cached; + } + final float value; + if (ENABLE_DEVICE_CONFIG) { + value = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_TEXTCLASSIFIER, + key, + getLegacySettings().getFloat(key, defaultValue)); + } else { + value = getLegacySettings().getFloat(key, defaultValue); + } + mCache.put(key, value); + return value; } } /** - * Reads a string flag. + * Reads a string setting through the cache. */ public String getString(String key, String defaultValue) { - if (ENABLE_DEVICE_CONFIG) { - return DeviceConfig.getString( - DeviceConfig.NAMESPACE_TEXTCLASSIFIER, - key, - mParser.getString(key, defaultValue)); + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof String) { + return (String) cached; + } + final String value; + if (ENABLE_DEVICE_CONFIG) { + value = DeviceConfig.getString( + DeviceConfig.NAMESPACE_TEXTCLASSIFIER, + key, + getLegacySettings().getString(key, defaultValue)); + } else { + value = getLegacySettings().getString(key, defaultValue); + } + mCache.put(key, value); + return value; + } + } + + /** + * Reads a string list setting through the cache. + */ + public List<String> getStringList(String key, List<String> defaultValue) { + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof List) { + final List asList = (List) cached; + if (asList.isEmpty()) { + return Collections.emptyList(); + } else if (asList.get(0) instanceof String) { + return (List<String>) cached; + } + } + final List<String> value; + if (ENABLE_DEVICE_CONFIG) { + value = getDeviceConfigStringList( + key, + getSettingsStringList(key, defaultValue)); + } else { + value = getSettingsStringList(key, defaultValue); + } + mCache.put(key, value); + return value; + } + } + + /** + * Reads a float array through the cache. The returned array should be expected to be of the + * same length as that of the defaultValue. + */ + public float[] getFloatArray(String key, float[] defaultValue) { + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof float[]) { + return (float[]) cached; + } + final float[] value; + if (ENABLE_DEVICE_CONFIG) { + value = getDeviceConfigFloatArray( + key, + getSettingsFloatArray(key, defaultValue)); + } else { + value = getSettingsFloatArray(key, defaultValue); + } + mCache.put(key, value); + return value; + } + } + + private List<String> getSettingsStringList(String key, List<String> defaultValue) { + return parse(mSettingsParser.getString(key, null), defaultValue); + } + + private static List<String> getDeviceConfigStringList(String key, List<String> defaultValue) { + return parse( + DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null), + defaultValue); + } + + private static float[] getDeviceConfigFloatArray(String key, float[] defaultValue) { + return parse( + DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null), + defaultValue); + } + + private float[] getSettingsFloatArray(String key, float[] defaultValue) { + return parse(mSettingsParser.getString(key, null), defaultValue); + } + + private static List<String> parse(@Nullable String listStr, List<String> defaultValue) { + if (listStr != null) { + return Collections.unmodifiableList( + Arrays.asList(listStr.split(STRING_LIST_DELIMITER))); + } + return defaultValue; + } + + private static float[] parse(@Nullable String arrayStr, float[] defaultValue) { + if (arrayStr != null) { + final String[] split = arrayStr.split(STRING_LIST_DELIMITER); + if (split.length != defaultValue.length) { + return defaultValue; + } + final float[] result = new float[split.length]; + for (int i = 0; i < split.length; i++) { + try { + result[i] = Float.parseFloat(split[i]); + } catch (NumberFormatException e) { + return defaultValue; + } + } + return result; } else { - return mParser.getString(key, defaultValue); + return defaultValue; } } } diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java index 876e5cccb2e0..2964f515f005 100644 --- a/core/java/android/view/textclassifier/TextClassificationConstants.java +++ b/core/java/android/view/textclassifier/TextClassificationConstants.java @@ -16,58 +16,39 @@ package android.view.textclassifier; -import android.annotation.Nullable; - import com.android.internal.util.IndentingPrintWriter; import java.util.Arrays; -import java.util.Collections; import java.util.List; -import java.util.StringJoiner; +import java.util.function.Supplier; /** * TextClassifier specific settings. - * This is encoded as a key=value list, separated by commas. Ex: - * - * <pre> - * smart_linkify_enabled (boolean) - * system_textclassifier_enabled (boolean) - * model_dark_launch_enabled (boolean) - * smart_selection_enabled (boolean) - * smart_text_share_enabled (boolean) - * smart_linkify_enabled (boolean) - * smart_select_animation_enabled (boolean) - * suggest_selection_max_range_length (int) - * classify_text_max_range_length (int) - * generate_links_max_text_length (int) - * generate_links_log_sample_rate (int) - * entity_list_default (String[]) - * entity_list_not_editable (String[]) - * entity_list_editable (String[]) - * in_app_conversation_action_types_default (String[]) - * notification_conversation_action_types_default (String[]) - * lang_id_threshold_override (float) - * template_intent_factory_enabled (boolean) - * translate_in_classification_enabled (boolean) - * detect_languages_from_text_enabled (boolean) - * lang_id_context_settings (float[]) - * </pre> - * + * This is encoded as a key=value list, separated by commas. * <p> - * Type: string - * see also android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS - * * Example of setting the values for testing. + * <p> + * <pre> * adb shell settings put global text_classifier_constants \ * model_dark_launch_enabled=true,smart_selection_enabled=true, \ * entity_list_default=phone:address, \ * lang_id_context_settings=20:1.0:0.4 + * </pre> + * <p> + * Settings are also available in device config. These take precedence over those in settings + * global. + * <p> + * <pre> + * adb shell cmd device_config put textclassifier system_textclassifier_enabled true + * </pre> + * + * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS + * @see android.provider.DeviceConfig.NAMESPACE_TEXTCLASSIFIER * @hide */ +// TODO: Rename to TextClassifierSettings. public final class TextClassificationConstants { - private static final String LOG_TAG = TextClassifier.DEFAULT_LOG_TAG; - /** * Whether the smart linkify feature is enabled. */ @@ -188,29 +169,26 @@ public final class TextClassificationConstants { private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000; private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000; private static final int GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT = 100; - private static final String STRING_LIST_DELIMITER = ":"; - private static final String ENTITY_LIST_DEFAULT_VALUE = new StringJoiner(STRING_LIST_DELIMITER) - .add(TextClassifier.TYPE_ADDRESS) - .add(TextClassifier.TYPE_EMAIL) - .add(TextClassifier.TYPE_PHONE) - .add(TextClassifier.TYPE_URL) - .add(TextClassifier.TYPE_DATE) - .add(TextClassifier.TYPE_DATE_TIME) - .add(TextClassifier.TYPE_FLIGHT_NUMBER).toString(); - private static final String CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES = - new StringJoiner(STRING_LIST_DELIMITER) - .add(ConversationAction.TYPE_TEXT_REPLY) - .add(ConversationAction.TYPE_CREATE_REMINDER) - .add(ConversationAction.TYPE_CALL_PHONE) - .add(ConversationAction.TYPE_OPEN_URL) - .add(ConversationAction.TYPE_SEND_EMAIL) - .add(ConversationAction.TYPE_SEND_SMS) - .add(ConversationAction.TYPE_TRACK_FLIGHT) - .add(ConversationAction.TYPE_VIEW_CALENDAR) - .add(ConversationAction.TYPE_VIEW_MAP) - .add(ConversationAction.TYPE_ADD_CONTACT) - .add(ConversationAction.TYPE_COPY) - .toString(); + private static final List<String> ENTITY_LIST_DEFAULT_VALUE = Arrays.asList( + TextClassifier.TYPE_ADDRESS, + TextClassifier.TYPE_EMAIL, + TextClassifier.TYPE_PHONE, + TextClassifier.TYPE_URL, + TextClassifier.TYPE_DATE, + TextClassifier.TYPE_DATE_TIME, + TextClassifier.TYPE_FLIGHT_NUMBER); + private static final List<String> CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES = Arrays.asList( + ConversationAction.TYPE_TEXT_REPLY, + ConversationAction.TYPE_CREATE_REMINDER, + ConversationAction.TYPE_CALL_PHONE, + ConversationAction.TYPE_OPEN_URL, + ConversationAction.TYPE_SEND_EMAIL, + ConversationAction.TYPE_SEND_SMS, + ConversationAction.TYPE_TRACK_FLIGHT, + ConversationAction.TYPE_VIEW_CALENDAR, + ConversationAction.TYPE_VIEW_MAP, + ConversationAction.TYPE_ADD_CONTACT, + ConversationAction.TYPE_COPY); /** * < 0 : Not set. Use value from LangId model. * 0 - 1: Override value in LangId model. @@ -221,259 +199,185 @@ public final class TextClassificationConstants { private static final boolean TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT = true; private static final boolean TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT = true; private static final boolean DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT = true; - private static final String LANG_ID_CONTEXT_SETTINGS_DEFAULT = - new StringJoiner(STRING_LIST_DELIMITER).add("20").add("1.0").add("0.4").toString(); - - private final boolean mSystemTextClassifierEnabled; - private final boolean mLocalTextClassifierEnabled; - private final boolean mModelDarkLaunchEnabled; - private final boolean mSmartSelectionEnabled; - private final boolean mSmartTextShareEnabled; - private final boolean mSmartLinkifyEnabled; - private final boolean mSmartSelectionAnimationEnabled; - private final int mSuggestSelectionMaxRangeLength; - private final int mClassifyTextMaxRangeLength; - private final int mGenerateLinksMaxTextLength; - private final int mGenerateLinksLogSampleRate; - private final List<String> mEntityListDefault; - private final List<String> mEntityListNotEditable; - private final List<String> mEntityListEditable; - private final List<String> mInAppConversationActionTypesDefault; - private final List<String> mNotificationConversationActionTypesDefault; - private final float mLangIdThresholdOverride; - private final boolean mTemplateIntentFactoryEnabled; - private final boolean mTranslateInClassificationEnabled; - private final boolean mDetectLanguagesFromTextEnabled; - private final float[] mLangIdContextSettings; + private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[] {20f, 1.0f, 0.4f}; - private TextClassificationConstants(@Nullable String settings) { - ConfigParser configParser = new ConfigParser(settings); - mSystemTextClassifierEnabled = - configParser.getBoolean( - SYSTEM_TEXT_CLASSIFIER_ENABLED, - SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT); - mLocalTextClassifierEnabled = - configParser.getBoolean( - LOCAL_TEXT_CLASSIFIER_ENABLED, - LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT); - mModelDarkLaunchEnabled = - configParser.getBoolean( - MODEL_DARK_LAUNCH_ENABLED, - MODEL_DARK_LAUNCH_ENABLED_DEFAULT); - mSmartSelectionEnabled = - configParser.getBoolean( - SMART_SELECTION_ENABLED, - SMART_SELECTION_ENABLED_DEFAULT); - mSmartTextShareEnabled = - configParser.getBoolean( - SMART_TEXT_SHARE_ENABLED, - SMART_TEXT_SHARE_ENABLED_DEFAULT); - mSmartLinkifyEnabled = - configParser.getBoolean( - SMART_LINKIFY_ENABLED, - SMART_LINKIFY_ENABLED_DEFAULT); - mSmartSelectionAnimationEnabled = - configParser.getBoolean( - SMART_SELECT_ANIMATION_ENABLED, - SMART_SELECT_ANIMATION_ENABLED_DEFAULT); - mSuggestSelectionMaxRangeLength = - configParser.getInt( - SUGGEST_SELECTION_MAX_RANGE_LENGTH, - SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT); - mClassifyTextMaxRangeLength = - configParser.getInt( - CLASSIFY_TEXT_MAX_RANGE_LENGTH, - CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT); - mGenerateLinksMaxTextLength = - configParser.getInt( - GENERATE_LINKS_MAX_TEXT_LENGTH, - GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT); - mGenerateLinksLogSampleRate = - configParser.getInt( - GENERATE_LINKS_LOG_SAMPLE_RATE, - GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT); - mEntityListDefault = parseStringList( - configParser.getString( - ENTITY_LIST_DEFAULT, - ENTITY_LIST_DEFAULT_VALUE)); - mEntityListNotEditable = parseStringList( - configParser.getString( - ENTITY_LIST_NOT_EDITABLE, - ENTITY_LIST_DEFAULT_VALUE)); - mEntityListEditable = parseStringList( - configParser.getString( - ENTITY_LIST_EDITABLE, - ENTITY_LIST_DEFAULT_VALUE)); - mInAppConversationActionTypesDefault = parseStringList( - configParser.getString( - IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT, - CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES)); - mNotificationConversationActionTypesDefault = parseStringList( - configParser.getString( - NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT, - CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES)); - mLangIdThresholdOverride = - configParser.getFloat( - LANG_ID_THRESHOLD_OVERRIDE, - LANG_ID_THRESHOLD_OVERRIDE_DEFAULT); - mTemplateIntentFactoryEnabled = - configParser.getBoolean( - TEMPLATE_INTENT_FACTORY_ENABLED, - TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT); - mTranslateInClassificationEnabled = - configParser.getBoolean( - TRANSLATE_IN_CLASSIFICATION_ENABLED, - TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT); - mDetectLanguagesFromTextEnabled = - configParser.getBoolean( - DETECT_LANGUAGES_FROM_TEXT_ENABLED, - DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT); - mLangIdContextSettings = parseFloatArray( - configParser, - LANG_ID_CONTEXT_SETTINGS, - LANG_ID_CONTEXT_SETTINGS_DEFAULT); - } + private final ConfigParser mConfigParser; - /** Load from a settings string. */ - public static TextClassificationConstants loadFromString(String settings) { - return new TextClassificationConstants(settings); + public TextClassificationConstants(Supplier<String> legacySettingsSupplier) { + mConfigParser = new ConfigParser(legacySettingsSupplier); } public boolean isLocalTextClassifierEnabled() { - return mLocalTextClassifierEnabled; + return mConfigParser.getBoolean( + LOCAL_TEXT_CLASSIFIER_ENABLED, + LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT); } public boolean isSystemTextClassifierEnabled() { - return mSystemTextClassifierEnabled; + return mConfigParser.getBoolean( + SYSTEM_TEXT_CLASSIFIER_ENABLED, + SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT); } public boolean isModelDarkLaunchEnabled() { - return mModelDarkLaunchEnabled; + return mConfigParser.getBoolean( + MODEL_DARK_LAUNCH_ENABLED, + MODEL_DARK_LAUNCH_ENABLED_DEFAULT); } public boolean isSmartSelectionEnabled() { - return mSmartSelectionEnabled; + return mConfigParser.getBoolean( + SMART_SELECTION_ENABLED, + SMART_SELECTION_ENABLED_DEFAULT); } public boolean isSmartTextShareEnabled() { - return mSmartTextShareEnabled; + return mConfigParser.getBoolean( + SMART_TEXT_SHARE_ENABLED, + SMART_TEXT_SHARE_ENABLED_DEFAULT); } public boolean isSmartLinkifyEnabled() { - return mSmartLinkifyEnabled; + return mConfigParser.getBoolean( + SMART_LINKIFY_ENABLED, + SMART_LINKIFY_ENABLED_DEFAULT); } public boolean isSmartSelectionAnimationEnabled() { - return mSmartSelectionAnimationEnabled; + return mConfigParser.getBoolean( + SMART_SELECT_ANIMATION_ENABLED, + SMART_SELECT_ANIMATION_ENABLED_DEFAULT); } public int getSuggestSelectionMaxRangeLength() { - return mSuggestSelectionMaxRangeLength; + return mConfigParser.getInt( + SUGGEST_SELECTION_MAX_RANGE_LENGTH, + SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT); } public int getClassifyTextMaxRangeLength() { - return mClassifyTextMaxRangeLength; + return mConfigParser.getInt( + CLASSIFY_TEXT_MAX_RANGE_LENGTH, + CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT); } public int getGenerateLinksMaxTextLength() { - return mGenerateLinksMaxTextLength; + return mConfigParser.getInt( + GENERATE_LINKS_MAX_TEXT_LENGTH, + GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT); } public int getGenerateLinksLogSampleRate() { - return mGenerateLinksLogSampleRate; + return mConfigParser.getInt( + GENERATE_LINKS_LOG_SAMPLE_RATE, + GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT); } public List<String> getEntityListDefault() { - return mEntityListDefault; + return mConfigParser.getStringList( + ENTITY_LIST_DEFAULT, + ENTITY_LIST_DEFAULT_VALUE); } public List<String> getEntityListNotEditable() { - return mEntityListNotEditable; + return mConfigParser.getStringList( + ENTITY_LIST_NOT_EDITABLE, + ENTITY_LIST_DEFAULT_VALUE); } public List<String> getEntityListEditable() { - return mEntityListEditable; + return mConfigParser.getStringList( + ENTITY_LIST_EDITABLE, + ENTITY_LIST_DEFAULT_VALUE); } public List<String> getInAppConversationActionTypes() { - return mInAppConversationActionTypesDefault; + return mConfigParser.getStringList( + IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT, + CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES); } public List<String> getNotificationConversationActionTypes() { - return mNotificationConversationActionTypesDefault; + return mConfigParser.getStringList( + NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT, + CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES); } public float getLangIdThresholdOverride() { - return mLangIdThresholdOverride; + return mConfigParser.getFloat( + LANG_ID_THRESHOLD_OVERRIDE, + LANG_ID_THRESHOLD_OVERRIDE_DEFAULT); } public boolean isTemplateIntentFactoryEnabled() { - return mTemplateIntentFactoryEnabled; + return mConfigParser.getBoolean( + TEMPLATE_INTENT_FACTORY_ENABLED, + TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT); } public boolean isTranslateInClassificationEnabled() { - return mTranslateInClassificationEnabled; + return mConfigParser.getBoolean( + TRANSLATE_IN_CLASSIFICATION_ENABLED, + TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT); } public boolean isDetectLanguagesFromTextEnabled() { - return mDetectLanguagesFromTextEnabled; + return mConfigParser.getBoolean( + DETECT_LANGUAGES_FROM_TEXT_ENABLED, + DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT); } public float[] getLangIdContextSettings() { - return mLangIdContextSettings; - } - - private static List<String> parseStringList(String listStr) { - return Collections.unmodifiableList(Arrays.asList(listStr.split(STRING_LIST_DELIMITER))); - } - - private static float[] parseFloatArray( - ConfigParser configParser, String key, String defaultStr) { - final String str = configParser.getString(key, defaultStr); - final String[] defaultSplit = defaultStr.split(STRING_LIST_DELIMITER); - String[] split = str.split(STRING_LIST_DELIMITER); - if (split.length != defaultSplit.length) { - Log.v(LOG_TAG, "Error parsing " + key + " flag. Using defaults."); - split = defaultSplit; - } - final float[] result = new float[split.length]; - for (int i = 0; i < split.length; i++) { - try { - result[i] = Float.parseFloat(split[i]); - } catch (NumberFormatException e) { - Log.v(LOG_TAG, "Error parsing part of " + key + " flag. Using defaults."); - result[i] = Float.parseFloat(defaultSplit[i]); - } - } - return result; + return mConfigParser.getFloatArray( + LANG_ID_CONTEXT_SETTINGS, + LANG_ID_CONTEXT_SETTINGS_DEFAULT); } void dump(IndentingPrintWriter pw) { pw.println("TextClassificationConstants:"); pw.increaseIndent(); - pw.printPair("isLocalTextClassifierEnabled", mLocalTextClassifierEnabled); - pw.printPair("isSystemTextClassifierEnabled", mSystemTextClassifierEnabled); - pw.printPair("isModelDarkLaunchEnabled", mModelDarkLaunchEnabled); - pw.printPair("isSmartSelectionEnabled", mSmartSelectionEnabled); - pw.printPair("isSmartTextShareEnabled", mSmartTextShareEnabled); - pw.printPair("isSmartLinkifyEnabled", mSmartLinkifyEnabled); - pw.printPair("isSmartSelectionAnimationEnabled", mSmartSelectionAnimationEnabled); - pw.printPair("getSuggestSelectionMaxRangeLength", mSuggestSelectionMaxRangeLength); - pw.printPair("getClassifyTextMaxRangeLength", mClassifyTextMaxRangeLength); - pw.printPair("getGenerateLinksMaxTextLength", mGenerateLinksMaxTextLength); - pw.printPair("getGenerateLinksLogSampleRate", mGenerateLinksLogSampleRate); - pw.printPair("getEntityListDefault", mEntityListDefault); - pw.printPair("getEntityListNotEditable", mEntityListNotEditable); - pw.printPair("getEntityListEditable", mEntityListEditable); - pw.printPair("getInAppConversationActionTypes", mInAppConversationActionTypesDefault); - pw.printPair("getNotificationConversationActionTypes", - mNotificationConversationActionTypesDefault); - pw.printPair("getLangIdThresholdOverride", mLangIdThresholdOverride); - pw.printPair("isTemplateIntentFactoryEnabled", mTemplateIntentFactoryEnabled); - pw.printPair("isTranslateInClassificationEnabled", mTranslateInClassificationEnabled); - pw.printPair("isDetectLanguageFromTextEnabled", mDetectLanguagesFromTextEnabled); - pw.printPair("getLangIdContextSettings", Arrays.toString(mLangIdContextSettings)); + pw.printPair("classify_text_max_range_length", getClassifyTextMaxRangeLength()) + .println(); + pw.printPair("detect_language_from_text_enabled", isDetectLanguagesFromTextEnabled()) + .println(); + pw.printPair("entity_list_default", getEntityListDefault()) + .println(); + pw.printPair("entity_list_editable", getEntityListEditable()) + .println(); + pw.printPair("entity_list_not_editable", getEntityListNotEditable()) + .println(); + pw.printPair("generate_links_log_sample_rate", getGenerateLinksLogSampleRate()) + .println(); + pw.printPair("generate_links_max_text_length", getGenerateLinksMaxTextLength()) + .println(); + pw.printPair("in_app_conversation_action_types_default", getInAppConversationActionTypes()) + .println(); + pw.printPair("lang_id_context_settings", Arrays.toString(getLangIdContextSettings())) + .println(); + pw.printPair("lang_id_threshold_override", getLangIdThresholdOverride()) + .println(); + pw.printPair("local_textclassifier_enabled", isLocalTextClassifierEnabled()) + .println(); + pw.printPair("model_dark_launch_enabled", isModelDarkLaunchEnabled()) + .println(); + pw.printPair("notification_conversation_action_types_default", + getNotificationConversationActionTypes()).println(); + pw.printPair("smart_linkify_enabled", isSmartLinkifyEnabled()) + .println(); + pw.printPair("smart_select_animation_enabled", isSmartSelectionAnimationEnabled()) + .println(); + pw.printPair("smart_selection_enabled", isSmartSelectionEnabled()) + .println(); + pw.printPair("smart_text_share_enabled", isSmartTextShareEnabled()) + .println(); + pw.printPair("suggest_selection_max_range_length", getSuggestSelectionMaxRangeLength()) + .println(); + pw.printPair("system_textclassifier_enabled", isSystemTextClassifierEnabled()) + .println(); + pw.printPair("template_intent_factory_enabled", isTemplateIntentFactoryEnabled()) + .println(); + pw.printPair("translate_in_classification_enabled", isTranslateInClassificationEnabled()) + .println(); pw.decreaseIndent(); - pw.println(); } -} +}
\ No newline at end of file diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java index 042b943c60a3..95ca9deb2871 100644 --- a/core/java/android/view/textclassifier/TextClassificationManager.java +++ b/core/java/android/view/textclassifier/TextClassificationManager.java @@ -45,6 +45,9 @@ public final class TextClassificationManager { private static final String LOG_TAG = "TextClassificationManager"; + private static final TextClassificationConstants sDefaultSettings = + new TextClassificationConstants(() -> null); + private final Object mLock = new Object(); private final TextClassificationSessionFactory mDefaultSessionFactory = classificationContext -> new TextClassificationSession( @@ -129,9 +132,10 @@ public final class TextClassificationManager { private TextClassificationConstants getSettings() { synchronized (mLock) { if (mSettings == null) { - mSettings = TextClassificationConstants.loadFromString(Settings.Global.getString( - getApplicationContext().getContentResolver(), - Settings.Global.TEXT_CLASSIFIER_CONSTANTS)); + mSettings = new TextClassificationConstants( + () -> Settings.Global.getString( + getApplicationContext().getContentResolver(), + Settings.Global.TEXT_CLASSIFIER_CONSTANTS)); } return mSettings; } @@ -251,7 +255,11 @@ public final class TextClassificationManager { /** @hide */ @VisibleForTesting - public void invalidate() { + public void invalidateForTesting() { + invalidate(); + } + + private void invalidate() { synchronized (mLock) { mSettings = null; mLocalTextClassifier = null; @@ -280,9 +288,8 @@ public final class TextClassificationManager { if (tcm != null) { return tcm.getSettings(); } else { - return TextClassificationConstants.loadFromString(Settings.Global.getString( - context.getApplicationContext().getContentResolver(), - Settings.Global.TEXT_CLASSIFIER_CONSTANTS)); + // Use default settings if there is no tcm. + return sDefaultSettings; } } diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index 3297523b0da9..3e95f1baf4bb 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -301,7 +301,7 @@ public final class TextClassifierImpl implements TextClassifier { final ZonedDateTime refTime = ZonedDateTime.now(); final Collection<String> entitiesToIdentify = request.getEntityConfig() != null ? request.getEntityConfig().resolveEntityListModifications( - getEntitiesForHints(request.getEntityConfig().getHints())) + getEntitiesForHints(request.getEntityConfig().getHints())) : mSettings.getEntityListDefault(); final String localesString = concatenateLocales(request.getDefaultLocales()); final String detectLanguageTags = detectLanguageTagsFromText(request.getText()); @@ -779,8 +779,8 @@ public final class TextClassifierImpl implements TextClassifier { final float moreTextScoreRatio = 1f - subjectTextScoreRatio; Log.v(LOG_TAG, String.format(Locale.US, "LangIdContextSettings: " - + "minimumTextSize=%d, penalizeRatio=%.2f, " - + "subjectTextScoreRatio=%.2f, moreTextScoreRatio=%.2f", + + "minimumTextSize=%d, penalizeRatio=%.2f, " + + "subjectTextScoreRatio=%.2f, moreTextScoreRatio=%.2f", minimumTextSize, penalizeRatio, subjectTextScoreRatio, moreTextScoreRatio)); if (end - start < minimumTextSize && penalizeRatio <= 0) { @@ -903,4 +903,3 @@ public final class TextClassifierImpl implements TextClassifier { } } } - diff --git a/core/java/com/android/internal/app/AbstractResolverComparator.java b/core/java/com/android/internal/app/AbstractResolverComparator.java index 87d80d408924..9ac979b54716 100644 --- a/core/java/com/android/internal/app/AbstractResolverComparator.java +++ b/core/java/com/android/internal/app/AbstractResolverComparator.java @@ -43,7 +43,7 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen private static final boolean DEBUG = false; private static final String TAG = "AbstractResolverComp"; - private AfterCompute mAfterCompute; + protected AfterCompute mAfterCompute; protected final PackageManager mPm; protected final UsageStatsManager mUsm; protected String[] mAnnotations; @@ -70,11 +70,7 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen Log.d(TAG, "RANKER_SERVICE_RESULT"); } if (mHandler.hasMessages(RANKER_RESULT_TIMEOUT)) { - if (msg.obj != null) { - handleResultMessage(msg); - } else { - Log.e(TAG, "Receiving null prediction results."); - } + handleResultMessage(msg); mHandler.removeMessages(RANKER_RESULT_TIMEOUT); afterCompute(); } @@ -99,7 +95,6 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen mHttp = "http".equals(scheme) || "https".equals(scheme); mContentType = intent.getType(); getContentAnnotations(intent); - mPm = context.getPackageManager(); mUsm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); mDefaultBrowserPackageName = mHttp diff --git a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java index 3b4e1a0618a1..3fddfc8a4602 100644 --- a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java +++ b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java @@ -28,7 +28,7 @@ import android.content.Intent; import android.content.pm.ResolveInfo; import android.os.Message; import android.os.UserHandle; -import android.view.textclassifier.Log; +import android.util.Log; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; @@ -36,29 +36,47 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Executors; /** - * Uses an {@link AppPredictor} to sort Resolver targets. + * Uses an {@link AppPredictor} to sort Resolver targets. If the AppPredictionService appears to be + * disabled by returning an empty sorted target list, {@link AppPredictionServiceResolverComparator} + * will fallback to using a {@link ResolverRankerServiceResolverComparator}. */ class AppPredictionServiceResolverComparator extends AbstractResolverComparator { private static final String TAG = "APSResolverComparator"; + private static final boolean DEBUG = false; private final AppPredictor mAppPredictor; private final Context mContext; private final Map<ComponentName, Integer> mTargetRanks = new HashMap<>(); private final UserHandle mUser; + private final Intent mIntent; + private final String mReferrerPackage; + // If this is non-null (and this is not destroyed), it means APS is disabled and we should fall + // back to using the ResolverRankerService. + private ResolverRankerServiceResolverComparator mResolverRankerService; AppPredictionServiceResolverComparator( - Context context, Intent intent, AppPredictor appPredictor, UserHandle user) { + Context context, + Intent intent, + String referrerPackage, + AppPredictor appPredictor, + UserHandle user) { super(context, intent); mContext = context; + mIntent = intent; mAppPredictor = appPredictor; mUser = user; + mReferrerPackage = referrerPackage; } @Override int compare(ResolveInfo lhs, ResolveInfo rhs) { + if (mResolverRankerService != null) { + return mResolverRankerService.compare(lhs, rhs); + } Integer lhsRank = mTargetRanks.get(new ComponentName(lhs.activityInfo.packageName, lhs.activityInfo.name)); Integer rhsRank = mTargetRanks.get(new ComponentName(rhs.activityInfo.packageName, @@ -75,33 +93,59 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator @Override void doCompute(List<ResolvedComponentInfo> targets) { + if (targets.isEmpty()) { + mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT); + return; + } List<AppTarget> appTargets = new ArrayList<>(); for (ResolvedComponentInfo target : targets) { appTargets.add(new AppTarget.Builder(new AppTargetId(target.name.flattenToString())) .setTarget(target.name.getPackageName(), mUser) .setClassName(target.name.getClassName()).build()); } - mAppPredictor.sortTargets(appTargets, mContext.getMainExecutor(), + mAppPredictor.sortTargets(appTargets, Executors.newSingleThreadExecutor(), sortedAppTargets -> { - Message msg = + if (sortedAppTargets.isEmpty()) { + if (DEBUG) { + Log.d(TAG, "AppPredictionService disabled. Using resolver."); + } + // APS for chooser is disabled. Fallback to resolver. + mResolverRankerService = + new ResolverRankerServiceResolverComparator( + mContext, mIntent, mReferrerPackage, + () -> mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT)); + mResolverRankerService.compute(targets); + } else { + if (DEBUG) { + Log.d(TAG, "AppPredictionService response received"); + } + Message msg = Message.obtain(mHandler, RANKER_SERVICE_RESULT, sortedAppTargets); - msg.sendToTarget(); - }); + msg.sendToTarget(); + } + } + ); } @Override void handleResultMessage(Message msg) { - if (msg.what == RANKER_SERVICE_RESULT) { + // Null value is okay if we have defaulted to the ResolverRankerService. + if (msg.what == RANKER_SERVICE_RESULT && msg.obj != null) { final List<AppTarget> sortedAppTargets = (List<AppTarget>) msg.obj; for (int i = 0; i < sortedAppTargets.size(); i++) { mTargetRanks.put(new ComponentName(sortedAppTargets.get(i).getPackageName(), sortedAppTargets.get(i).getClassName()), i); } + } else if (msg.obj == null && mResolverRankerService == null) { + Log.e(TAG, "Unexpected null result"); } } @Override float getScore(ComponentName name) { + if (mResolverRankerService != null) { + return mResolverRankerService.getScore(name); + } Integer rank = mTargetRanks.get(name); if (rank == null) { Log.w(TAG, "Score requested for unknown component."); @@ -113,6 +157,10 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator @Override void updateModel(ComponentName componentName) { + if (mResolverRankerService != null) { + mResolverRankerService.updateModel(componentName); + return; + } mAppPredictor.notifyAppTargetEvent( new AppTargetEvent.Builder( new AppTarget.Builder( @@ -121,4 +169,12 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator .setClassName(componentName.getClassName()).build(), ACTION_LAUNCH).build()); } + + @Override + void destroy() { + if (mResolverRankerService != null) { + mResolverRankerService.destroy(); + mResolverRankerService = null; + } + } } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 1eabbd8bd00e..2349c31f590f 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -153,8 +153,8 @@ public class ChooserActivity extends ResolverActivity { * {@link AppPredictionManager} will be queried for direct share targets. */ // TODO(b/123089490): Replace with system flag - private static final boolean USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS = false; - private static final boolean USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES = false; + private static final boolean USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS = true; + private static final boolean USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES = true; // TODO(b/123088566) Share these in a better way. private static final String APP_PREDICTION_SHARE_UI_SURFACE = "share"; public static final String LAUNCH_LOCATON_DIRECT_SHARE = "direct_share"; @@ -453,6 +453,11 @@ public class ChooserActivity extends ResolverActivity { if (mChooserListAdapter == null) { return; } + if (resultList.isEmpty()) { + // APS may be disabled, so try querying targets ourselves. + queryDirectShareTargets(mChooserListAdapter, true); + return; + } final List<DisplayResolveInfo> driList = getDisplayResolveInfos(mChooserListAdapter); final List<ShortcutManager.ShareShortcutInfo> shareShortcutInfos = @@ -919,6 +924,8 @@ public class ChooserActivity extends ResolverActivity { mChooserHandler.removeMessages(LIST_VIEW_UPDATE_MESSAGE); mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_RESULT); + mChooserHandler.removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT); + mChooserHandler.removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED); if (mAppPredictor != null) { mAppPredictor.unregisterPredictionUpdates(mAppPredictorCallback); mAppPredictor.destroy(); @@ -1272,11 +1279,14 @@ public class ChooserActivity extends ResolverActivity { return driList; } - private void queryDirectShareTargets(ChooserListAdapter adapter) { - AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(); - if (appPredictor != null) { - appPredictor.requestPredictionUpdate(); - return; + private void queryDirectShareTargets( + ChooserListAdapter adapter, boolean skipAppPredictionService) { + if (!skipAppPredictionService) { + AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(); + if (appPredictor != null) { + appPredictor.requestPredictionUpdate(); + return; + } } // Default to just querying ShortcutManager if AppPredictor not present. final IntentFilter filter = getTargetIntentFilter(); @@ -1609,7 +1619,7 @@ public class ChooserActivity extends ResolverActivity { AbstractResolverComparator resolverComparator; if (appPredictor != null) { resolverComparator = new AppPredictionServiceResolverComparator(this, getTargetIntent(), - appPredictor, getUser()); + getReferrerPackageName(), appPredictor, getUser()); } else { resolverComparator = new ResolverRankerServiceResolverComparator(this, getTargetIntent(), @@ -2062,6 +2072,8 @@ public class ChooserActivity extends ResolverActivity { private static final int MAX_SERVICE_TARGETS = 8; + private int mNumShortcutResults = 0; + // Reserve spots for incoming direct share targets by adding placeholders private ChooserTargetInfo mPlaceHolderTargetInfo = new PlaceHolderTargetInfo(); private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>(); @@ -2205,7 +2217,7 @@ public class ChooserActivity extends ResolverActivity { Log.d(TAG, "querying direct share targets from ShortcutManager"); } - queryDirectShareTargets(this); + queryDirectShareTargets(this, false); } if (USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS) { if (DEBUG) { @@ -2397,9 +2409,15 @@ public class ChooserActivity extends ResolverActivity { // This incents ChooserTargetServices to define what's truly better. targetScore = lastScore * 0.95f; } - shouldNotify |= insertServiceTarget( + boolean isInserted = insertServiceTarget( new SelectableTargetInfo(origTarget, target, targetScore)); + if (isInserted && isShortcutResult) { + mNumShortcutResults++; + } + + shouldNotify |= isInserted; + if (DEBUG) { Log.d(TAG, " => " + target.toString() + " score=" + targetScore + " base=" + target.getScore() @@ -2415,6 +2433,10 @@ public class ChooserActivity extends ResolverActivity { } } + private int getNumShortcutResults() { + return mNumShortcutResults; + } + /** * Use the scoring system along with artificial boosts to create up to 3 distinct buckets: * <ol> @@ -2871,10 +2893,10 @@ public class ChooserActivity extends ResolverActivity { if (startType != lastStartType || rowPosition == getContentPreviewRowCount() + getProfileRowCount()) { - row.setBackground( + row.setForeground( getResources().getDrawable(R.drawable.chooser_row_layer_list, null)); } else { - row.setBackground(null); + row.setForeground(null); } int columnCount = holder.getColumnCount(); @@ -2945,7 +2967,15 @@ public class ChooserActivity extends ResolverActivity { } public void handleScroll(View v, int y, int oldy) { - if (mDirectShareViewHolder != null) { + // Only expand direct share area if there is a minimum number of shortcuts, + // which will help reduce the amount of visible shuffling due to older-style + // direct share targets. + int orientation = getResources().getConfiguration().orientation; + boolean canExpandDirectShare = + mChooserListAdapter.getNumShortcutResults() > getMaxTargetsPerRow() + && orientation == Configuration.ORIENTATION_PORTRAIT; + + if (mDirectShareViewHolder != null && canExpandDirectShare) { mDirectShareViewHolder.handleScroll(mAdapterView, y, oldy, getMaxTargetsPerRow()); } } diff --git a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java index a7819070ee98..01e0622063cd 100644 --- a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java +++ b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java @@ -66,9 +66,6 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator // timeout for establishing connections with a ResolverRankerService. private static final int CONNECTION_COST_TIMEOUT_MILLIS = 200; - // timeout for establishing connections with a ResolverRankerService, collecting features and - // predicting ranking scores. - private static final int WATCHDOG_TIMEOUT_MILLIS = 500; private final Collator mCollator; private final Map<String, UsageStats> mStats; @@ -106,6 +103,10 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator if (msg.what != RANKER_SERVICE_RESULT) { return; } + if (msg.obj == null) { + Log.e(TAG, "Receiving null prediction results."); + return; + } final List<ResolverTarget> receivedTargets = (List<ResolverTarget>) msg.obj; if (receivedTargets != null && mTargets != null && receivedTargets.size() == mTargets.size()) { diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java index 9e8bd640026b..cc2caca49276 100644 --- a/core/java/com/android/internal/content/FileSystemProvider.java +++ b/core/java/com/android/internal/content/FileSystemProvider.java @@ -60,9 +60,11 @@ import java.nio.file.FileVisitor; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; /** * A helper class for {@link android.provider.DocumentsProvider} to perform file operations on local @@ -613,28 +615,28 @@ public abstract class FileSystemProvider extends DocumentsProvider { return projection == null ? mDefaultProjection : projection; } - private void startObserving(File file, Uri notifyUri) { + private void startObserving(File file, Uri notifyUri, DirectoryCursor cursor) { synchronized (mObservers) { DirectoryObserver observer = mObservers.get(file); if (observer == null) { - observer = new DirectoryObserver( - file, getContext().getContentResolver(), notifyUri); + observer = + new DirectoryObserver(file, getContext().getContentResolver(), notifyUri); observer.startWatching(); mObservers.put(file, observer); } - observer.mRefCount++; + observer.mCursors.add(cursor); if (LOG_INOTIFY) Log.d(TAG, "after start: " + observer); } } - private void stopObserving(File file) { + private void stopObserving(File file, DirectoryCursor cursor) { synchronized (mObservers) { DirectoryObserver observer = mObservers.get(file); if (observer == null) return; - observer.mRefCount--; - if (observer.mRefCount == 0) { + observer.mCursors.remove(cursor); + if (observer.mCursors.size() == 0) { mObservers.remove(file); observer.stopWatching(); } @@ -650,27 +652,31 @@ public abstract class FileSystemProvider extends DocumentsProvider { private final File mFile; private final ContentResolver mResolver; private final Uri mNotifyUri; + private final CopyOnWriteArrayList<DirectoryCursor> mCursors; - private int mRefCount = 0; - - public DirectoryObserver(File file, ContentResolver resolver, Uri notifyUri) { + DirectoryObserver(File file, ContentResolver resolver, Uri notifyUri) { super(file.getAbsolutePath(), NOTIFY_EVENTS); mFile = file; mResolver = resolver; mNotifyUri = notifyUri; + mCursors = new CopyOnWriteArrayList<>(); } @Override public void onEvent(int event, String path) { if ((event & NOTIFY_EVENTS) != 0) { if (LOG_INOTIFY) Log.d(TAG, "onEvent() " + event + " at " + path); + for (DirectoryCursor cursor : mCursors) { + cursor.notifyChanged(); + } mResolver.notifyChange(mNotifyUri, null, false); } } @Override public String toString() { - return "DirectoryObserver{file=" + mFile.getAbsolutePath() + ", ref=" + mRefCount + "}"; + String filePath = mFile.getAbsolutePath(); + return "DirectoryObserver{file=" + filePath + ", ref=" + mCursors.size() + "}"; } } @@ -681,16 +687,22 @@ public abstract class FileSystemProvider extends DocumentsProvider { super(columnNames); final Uri notifyUri = buildNotificationUri(docId); - setNotificationUri(getContext().getContentResolver(), notifyUri); + boolean registerSelfObserver = false; // Our FileObserver sees all relevant changes. + setNotificationUris(getContext().getContentResolver(), Arrays.asList(notifyUri), + getContext().getContentResolver().getUserId(), registerSelfObserver); mFile = file; - startObserving(mFile, notifyUri); + startObserving(mFile, notifyUri, this); + } + + public void notifyChanged() { + onChange(false); } @Override public void close() { super.close(); - stopObserving(mFile); + stopObserving(mFile, this); } } } diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java index 5d08a259dace..ad1ff902b7d8 100644 --- a/core/java/com/android/internal/content/PackageHelper.java +++ b/core/java/com/android/internal/content/PackageHelper.java @@ -64,6 +64,8 @@ public class PackageHelper { public static final int RECOMMEND_MEDIA_UNAVAILABLE = -5; public static final int RECOMMEND_FAILED_INVALID_URI = -6; public static final int RECOMMEND_FAILED_VERSION_DOWNGRADE = -7; + /** {@hide} */ + public static final int RECOMMEND_FAILED_WRONG_INSTALLED_VERSION = -8; private static final String TAG = "PackageHelper"; // App installation location settings values diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index dd6fb72888e2..2987b4e56fec 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -406,8 +406,13 @@ class ZygoteConnection { private int mHiddenApiAccessStatslogSampleRate = 0; public static void setHiddenApiAccessLogSampleRates(int sampleRate, int newSampleRate) { - sInstance.mHiddenApiAccessLogSampleRate = sampleRate; - sInstance.mHiddenApiAccessStatslogSampleRate = newSampleRate; + if (sampleRate != -1) { + sInstance.mHiddenApiAccessLogSampleRate = sampleRate; + } + + if (newSampleRate != -1) { + sInstance.mHiddenApiAccessStatslogSampleRate = newSampleRate; + } } public static HiddenApiUsageLogger getInstance() { diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index cfc32cf67016..7e501d27d30f 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -50,9 +50,11 @@ oneway interface IStatusBar * @param mask which flags to change * @param fullscreenBounds the current bounds of the fullscreen stack, in screen coordinates * @param dockedBounds the current bounds of the docked stack, in screen coordinates + * @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME. */ void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis, - int mask, in Rect fullscreenBounds, in Rect dockedBounds); + int mask, in Rect fullscreenBounds, in Rect dockedBounds, + boolean navbarColorManagedByIme); void topAppWindowChanged(int displayId, boolean menuVisible); void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition, diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java index ff94264d9b90..6b0f8b2f5dc9 100644 --- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java +++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java @@ -39,12 +39,13 @@ public final class RegisterStatusBarResult implements Parcelable { public final IBinder mImeToken; public final Rect mFullscreenStackBounds; public final Rect mDockedStackBounds; + public final boolean mNavbarColorManagedByIme; public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1, int systemUiVisibility, boolean menuVisible, int imeWindowVis, int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2, int fullscreenStackSysUiVisibility, int dockedStackSysUiVisibility, IBinder imeToken, Rect fullscreenStackBounds, - Rect dockedStackBounds) { + Rect dockedStackBounds, boolean navbarColorManagedByIme) { mIcons = new ArrayMap<>(icons); mDisabledFlags1 = disabledFlags1; mSystemUiVisibility = systemUiVisibility; @@ -58,6 +59,7 @@ public final class RegisterStatusBarResult implements Parcelable { mImeToken = imeToken; mFullscreenStackBounds = fullscreenStackBounds; mDockedStackBounds = dockedStackBounds; + mNavbarColorManagedByIme = navbarColorManagedByIme; } @Override @@ -80,6 +82,7 @@ public final class RegisterStatusBarResult implements Parcelable { dest.writeStrongBinder(mImeToken); dest.writeTypedObject(mFullscreenStackBounds, flags); dest.writeTypedObject(mDockedStackBounds, flags); + dest.writeBoolean(mNavbarColorManagedByIme); } /** @@ -103,11 +106,12 @@ public final class RegisterStatusBarResult implements Parcelable { final IBinder imeToken = source.readStrongBinder(); final Rect fullscreenStackBounds = source.readTypedObject(Rect.CREATOR); final Rect dockedStackBounds = source.readTypedObject(Rect.CREATOR); + final boolean navbarColorManagedByIme = source.readBoolean(); return new RegisterStatusBarResult(icons, disabledFlags1, systemUiVisibility, menuVisible, imeWindowVis, imeBackDisposition, showImeSwitcher, disabledFlags2, fullscreenStackSysUiVisibility, dockedStackSysUiVisibility, imeToken, fullscreenStackBounds, - dockedStackBounds); + dockedStackBounds, navbarColorManagedByIme); } @Override diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp index cb55618c685c..e5b72caf2792 100644 --- a/core/jni/android_os_HwBlob.cpp +++ b/core/jni/android_os_HwBlob.cpp @@ -88,7 +88,7 @@ JHwBlob::JHwBlob(JNIEnv *env, jobject thiz, size_t size) mOwnsBuffer(true), mHandle(0) { if (size > 0) { - mBuffer = malloc(size); + mBuffer = calloc(size, 1); } } diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index c4c16ee0ef9b..43c0bbecd71c 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -546,7 +546,7 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, if (CC_LIKELY(transform.isPureTranslate())) { // snap/round the computed bounds, so they match the rounding behavior // of the clear done in SurfaceView#draw(). - bounds.snapToPixelBoundaries(); + bounds.snapGeometryToPixelBoundaries(false); } else { // Conservatively round out so the punched hole (in the ZOrderOnTop = true case) // doesn't extend beyond the other window diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index ecc9d648ac0d..986771dad802 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -136,8 +136,8 @@ public: // ColorSpace.Named.SRGB.ordinal() = 0; static constexpr jint SRGB = 0; - // ColorSpace.Named.DISPLAY_P3.ordinal() = 6; - static constexpr jint DISPLAY_P3 = 6; + // ColorSpace.Named.DISPLAY_P3.ordinal() = 7; + static constexpr jint DISPLAY_P3 = 7; }; constexpr jint fromDataspaceToNamedColorSpaceValue(const ui::Dataspace dataspace) { diff --git a/core/proto/OWNERS b/core/proto/OWNERS index 480b1eaaf98c..a3d4798647be 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -1,13 +1,8 @@ # Be sure you are familiar with proto when you modify this directory. # Metrics -bookatz@google.com -cjyu@google.com -jinyithu@google.com joeo@google.com -kwekua@google.com singhtejinder@google.com -yanglu@google.com yaochen@google.com yro@google.com diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 1ff7418a3ef8..9cffb2bf1601 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2354,4 +2354,11 @@ enum PageId { // OPEN: Settings > Pick preferred SIM dialog DIALOG_PREFERRED_SIM_PICKER = 1709; + + // OPEN: Settings > Network & internet > Mobile network > Delete sim + DIALOG_DELETE_SIM_CONFIRMATION = 1713; + + // OPEN: Settings > Network & internet > Mobile network > Delete sim > (answer yes to + // confirmation) + DIALOG_DELETE_SIM_PROGRESS = 1714; } diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index dbd219141e3e..e6ae22658f31 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -159,6 +159,9 @@ message DisplayContentProto { optional int32 surface_size = 14 [deprecated=true]; optional string focused_app = 15; optional AppTransitionProto app_transition = 16; + repeated IdentifierProto opening_apps = 17; + repeated IdentifierProto closing_apps = 18; + repeated IdentifierProto changing_apps = 19; } /* represents DisplayFrames */ diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto index 0db74243222c..0821d147044d 100644 --- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto +++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto @@ -149,4 +149,6 @@ enum EventId { PROVISIONING_PREPARE_STARTED = 122; PROVISIONING_PREPARE_COMPLETED = 123; PROVISIONING_FLOW_TYPE = 124; + CROSS_PROFILE_APPS_GET_TARGET_USER_PROFILES = 125; + CROSS_PROFILE_APPS_START_ACTIVITY_AS_USER = 126; } diff --git a/core/proto/android/stats/mediametrics/mediametrics.proto b/core/proto/android/stats/mediametrics/mediametrics.proto new file mode 100644 index 000000000000..34ed90a8c90c --- /dev/null +++ b/core/proto/android/stats/mediametrics/mediametrics.proto @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2019 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. + */ + +syntax = "proto2"; + +package android.stats.mediametrics; + +/** + * Track how we arbitrate between microphone/input requests. + * Logged from + * frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp + * frameworks/av/services/mediaanalytics/statsd_audiopolicy.cpp + * Next Tag: 10 + */ +message AudioPolicyData { + optional int32 status = 1; + optional string request_source = 2; + optional string request_package = 3; + optional int32 request_session = 4; + optional string request_device = 5; + optional string active_source = 6; + optional string active_package = 7; + optional int32 active_session = 8; + optional string active_device = 9; +} + +/** + * Track properties of audio recording + * Logged from + * frameworks/av/media/libaudioclient/AudioRecord.cpp + * frameworks/av/services/mediaanalytics/statsd_audiorecord.cpp + * Next Tag: 16 + */ +message AudioRecordData { + optional string encoding = 1; + optional string source = 2; + optional int32 latency = 3; + optional int32 samplerate = 4; + optional int32 channels = 5; + optional int64 created_millis = 6; + optional int64 duration_millis = 7; + optional int32 count = 8; + optional int32 error_code = 9; + optional string error_function = 10; + optional int32 port_id = 11; + optional int32 frame_count = 12; + optional string attributes = 13; + optional int64 channel_mask = 14; + optional int64 start_count = 15; + +} + +/** + * Track audio thread performance data + * Logged from + * frameworks/av/media/libnblog/ReportPerformance.cpp + * frameworks/av/services/mediaanalytics/statsd_audiothread.cpp + * Next Tag: 28 + */ +message AudioThreadData { + optional string type = 1; + optional int32 framecount = 2; + optional int32 samplerate = 3; + optional string work_millis_hist = 4; + optional string latency_millis_hist = 5; + optional string warmup_millis_hist = 6; + optional int64 underruns = 7; + optional int64 overruns = 8; + optional int64 active_millis = 9; + optional int64 duration_millis = 10; + + optional int32 id = 11; + optional int32 port_id = 12; + optional int32 sample_rate = 13; + optional int64 channel_mask = 14; + optional string encoding = 15; + optional int32 frame_count = 16; + optional string output_device = 17; + optional string input_device = 18; + optional double io_jitter_mean_millis = 19; + optional double io_jitter_stddev_millis = 20; + optional double process_time_mean_millis = 21; + optional double process_time_stddev_millis = 22; + optional double timestamp_jitter_mean_millis = 23; + optional double timestamp_jitter_stddev_millis = 24; + optional double latency_mean_millis = 25; + optional double latency_stddev_millis = 26; + +} + +/** + * Track audio track playback data + * Logged from + * frameworks/av/media/libaudioclient/AudioTrack.cpp + * frameworks/av/services/mediaanalytics/statsd_audiotrack.cpp + * Next Tag: 12 + */ +message AudioTrackData { + optional string stream_type = 1; + optional string content_type = 2; + optional string track_usage = 3; + optional int32 sample_rate = 4; + optional int64 channel_mask = 5; + + optional int32 underrun_frames = 6; + optional int32 startup_glitch = 7; + + optional int32 port_id = 8; + optional string encoding = 9; + optional int32 frame_count = 10; + optional string attributes = 11; + + +} + +/** + * Track Media Codec usage + * Logged from: + * frameworks/av/media/libstagefright/MediaCodec.cpp + * frameworks/av/services/mediaanalytics/statsd_codec.cpp + * Next Tag: 21 + */ +message CodecData { + optional string codec = 1; + optional string mime = 2; + optional string mode = 3; + optional int32 encoder = 4; + optional int32 secure = 5; + optional int32 width = 6; + optional int32 height = 7; + optional int32 rotation = 8; + optional int32 crypto = 9; + optional int32 profile = 10; + optional int32 level = 11; + optional int32 max_width = 12; + optional int32 max_height = 13; + optional int32 error_code = 14; + optional string error_state = 15; + optional int64 latency_max = 16; + optional int64 latency_min = 17; + optional int64 latency_avg = 18; + optional int64 latency_count = 19; + optional int64 latency_unknown = 20; +} + +/** + * Track Media Extractor (pulling video/audio streams out of containers) usage + * Logged from: + * frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp + * frameworks/av/services/mediaanalytics/statsd_extractor.cpp + * Next Tag: 4 + */ +message ExtractorData { + optional string format = 1; + optional string mime = 2; + optional int32 tracks = 3; +} + +/** + * Track Media Player usage + * this handles both nuplayer and nuplayer2 + * Logged from: + * frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp + * frameworks/av/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp + * frameworks/av/services/mediaanalytics/statsd_nuplayer.cpp + * Next Tag: 21 + */ +message NuPlayerData { + optional string whichPlayer = 1; + + optional string video_mime = 2; + optional string video_codec = 3; + optional int32 width = 4; + optional int32 height = 5; + optional int64 frames = 6; + optional int64 frames_dropped = 7; + optional double framerate = 8; + optional string audio_mime = 9; + optional string audio_codec = 10; + optional int64 duration_millis = 11; + optional int64 playing_millis = 12; + optional int32 error = 13; + optional int32 error_code = 14; + optional string error_state = 15; + optional string data_source_type = 16; + optional int64 rebuffering_millis = 17; + optional int32 rebuffers = 18; + optional int32 rebuffer_at_exit = 19; + optional int64 frames_dropped_startup = 20; +} + +/** + * Track information about recordings (e.g. camcorder) + * Logged from + * frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp + * frameworks/av/services/mediaanalytics/if_statsd.cpp + * Next Tag: 22 + */ +message RecorderData { + optional string audio_mime = 1; + optional string video_mime = 2; + optional int32 video_profile = 3; + optional int32 video_level = 4; + optional int32 width = 5; + optional int32 height = 6; + optional int32 rotation = 7; + optional int32 framerate = 8; + optional int32 capture_fps = 9; + optional double capture_fps_enable = 10; + optional int64 duration_millis = 11; + optional int64 paused_millis = 12; + optional int32 paused_count = 13; + optional int32 audio_bitrate = 14; + optional int32 audio_channels = 15; + optional int32 audio_samplerate = 16; + optional int32 movie_timescale = 17; + optional int32 audio_timescale = 18; + optional int32 video_timescale = 19; + optional int32 video_bitrate = 20; + optional int32 iframe_interval = 21; +} diff --git a/core/res/res/anim/ic_bluetooth_transient_animation_0.xml b/core/res/res/anim/ic_bluetooth_transient_animation_0.xml new file mode 100644 index 000000000000..f14cfcf2beb1 --- /dev/null +++ b/core/res/res/anim/ic_bluetooth_transient_animation_0.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="0" android:valueFrom="1" android:valueTo="0.5" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="233" + android:startOffset="17" android:valueFrom="0.5" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="250" android:valueFrom="0.5" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="233" + android:startOffset="267" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="500" android:valueFrom="1" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="233" + android:startOffset="517" android:valueFrom="0.5" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="750" android:valueFrom="0.5" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> +</set> diff --git a/core/res/res/anim/ic_bluetooth_transient_animation_1.xml b/core/res/res/anim/ic_bluetooth_transient_animation_1.xml new file mode 100644 index 000000000000..934bd1795765 --- /dev/null +++ b/core/res/res/anim/ic_bluetooth_transient_animation_1.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="0" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="250" android:valueFrom="1" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="233" + android:startOffset="267" android:valueFrom="0.5" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="500" android:valueFrom="0.5" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="233" + android:startOffset="517" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="750" android:valueFrom="1" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> +</set> diff --git a/core/res/res/anim/ic_bluetooth_transient_animation_2.xml b/core/res/res/anim/ic_bluetooth_transient_animation_2.xml new file mode 100644 index 000000000000..7dce34cc0926 --- /dev/null +++ b/core/res/res/anim/ic_bluetooth_transient_animation_2.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="translateX" android:duration="1000" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> +</set> diff --git a/core/res/res/anim/ic_hotspot_transient_animation_0.xml b/core/res/res/anim/ic_hotspot_transient_animation_0.xml new file mode 100644 index 000000000000..d5a4c521e7ff --- /dev/null +++ b/core/res/res/anim/ic_hotspot_transient_animation_0.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="0" android:valueFrom="1" android:valueTo="0.5" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="633" + android:startOffset="17" android:valueFrom="0.5" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="650" android:valueFrom="0.5" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> +</set> diff --git a/core/res/res/anim/ic_hotspot_transient_animation_1.xml b/core/res/res/anim/ic_hotspot_transient_animation_1.xml new file mode 100644 index 000000000000..36db4b956e90 --- /dev/null +++ b/core/res/res/anim/ic_hotspot_transient_animation_1.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="200" + android:startOffset="0" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="200" android:valueFrom="1" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="617" + android:startOffset="217" android:valueFrom="0.5" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="833" android:valueFrom="0.5" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> +</set> diff --git a/core/res/res/anim/ic_hotspot_transient_animation_2.xml b/core/res/res/anim/ic_hotspot_transient_animation_2.xml new file mode 100644 index 000000000000..3d6743608c87 --- /dev/null +++ b/core/res/res/anim/ic_hotspot_transient_animation_2.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="400" + android:startOffset="0" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="400" android:valueFrom="1" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="617" + android:startOffset="417" android:valueFrom="0.5" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="1033" android:valueFrom="0.5" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> +</set> diff --git a/core/res/res/anim/ic_hotspot_transient_animation_3.xml b/core/res/res/anim/ic_hotspot_transient_animation_3.xml new file mode 100644 index 000000000000..1495dc98d852 --- /dev/null +++ b/core/res/res/anim/ic_hotspot_transient_animation_3.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="translateX" android:duration="1250" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> +</set> diff --git a/packages/SystemUI/res/drawable/ic_bluetooth_transient_animation.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_0.xml index dc3c3e24c68a..f71d0ee30def 100644 --- a/packages/SystemUI/res/drawable/ic_bluetooth_transient_animation.xml +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_0.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2017 The Android Open Source Project +<!-- + Copyright (C) 2019 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. @@ -13,13 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. --> -<animated-vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:drawable="@drawable/ic_bluetooth_transient" > - <target - android:name="dot_left" - android:animation="@anim/ic_bluetooth_transient_dot_left_animation" /> - <target - android:name="dot_right" - android:animation="@anim/ic_bluetooth_transient_dot_right_animation" /> -</animated-vector> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="scaleY" android:duration="0" + android:startOffset="167" android:valueFrom="1" android:valueTo="0" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_sysbar_menu.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_1.xml index d53c95b1cb80..9af42a10cbe9 100644 --- a/packages/SystemUI/res/drawable/ic_sysbar_menu.xml +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_1.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2018 The Android Open Source Project + Copyright (C) 2019 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. @@ -14,13 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="21dp" - android:height="21dp" - android:viewportWidth="28" - android:viewportHeight="28"> - - <path - android:fillColor="?attr/singleToneColor" - android:pathData="M14,9.5c1.24,0,2.25-1.01,2.25-2.25S15.24,5,14,5s-2.25,1.01-2.25,2.25S12.76,9.5,14,9.5z M14,11.75 c-1.24,0-2.25,1.01-2.25,2.25s1.01,2.25,2.25,2.25s2.25-1.01,2.25-2.25S15.24,11.75,14,11.75z M14,18.5 c-1.24,0-2.25,1.01-2.25,2.25S12.76,23,14,23s2.25-1.01,2.25-2.25S15.24,18.5,14,18.5z" /> -</vector>
\ No newline at end of file +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="scaleX" android:duration="0" + android:startOffset="167" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_2.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_2.xml new file mode 100644 index 000000000000..0e7864a4c71b --- /dev/null +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_2.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="scaleY" android:duration="0" + android:startOffset="333" android:valueFrom="1" android:valueTo="0" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_3.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_3.xml new file mode 100644 index 000000000000..bbec629c1a1e --- /dev/null +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_3.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="scaleX" android:duration="0" + android:startOffset="333" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_4.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_4.xml new file mode 100644 index 000000000000..2c921b1cec25 --- /dev/null +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_4.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="scaleY" android:duration="0" + android:startOffset="500" android:valueFrom="1" android:valueTo="0" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_5.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_5.xml new file mode 100644 index 000000000000..51aedc098785 --- /dev/null +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_5.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="scaleX" android:duration="0" + android:startOffset="500" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_6.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_6.xml new file mode 100644 index 000000000000..d1da131d4d1e --- /dev/null +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_6.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="scaleY" android:duration="0" + android:startOffset="667" android:valueFrom="1" android:valueTo="0" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_7.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_7.xml new file mode 100644 index 000000000000..0b6eb428f0ff --- /dev/null +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_7.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="scaleX" android:duration="0" + android:startOffset="667" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_8.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_8.xml new file mode 100644 index 000000000000..3469d43d4e67 --- /dev/null +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_8.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="translateX" android:duration="833" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_bluetooth_transient_animation.xml b/core/res/res/drawable/ic_bluetooth_transient_animation.xml new file mode 100644 index 000000000000..61b0f1ad39b3 --- /dev/null +++ b/core/res/res/drawable/ic_bluetooth_transient_animation.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/ic_bluetooth_transient_animation_drawable"> + <target android:name="_R_G_L_0_G_D_1_P_0" + android:animation="@anim/ic_bluetooth_transient_animation_0"/> + <target android:name="_R_G_L_0_G_D_2_P_0" + android:animation="@anim/ic_bluetooth_transient_animation_1"/> + <target android:name="time_group" android:animation="@anim/ic_bluetooth_transient_animation_2"/> +</animated-vector> diff --git a/core/res/res/drawable/ic_bluetooth_transient_animation_drawable.xml b/core/res/res/drawable/ic_bluetooth_transient_animation_drawable.xml new file mode 100644 index 000000000000..a520feabedf1 --- /dev/null +++ b/core/res/res/drawable/ic_bluetooth_transient_animation_drawable.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" + android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_0_G" android:translateX="-12" + android:translateY="-12"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M17.71 7.71 C17.71,7.71 12,2 12,2 C12,2 11,2 11,2 C11,2 11,9.59 11,9.59 C11,9.59 6.41,5 6.41,5 C6.41,5 5,6.41 5,6.41 C5,6.41 10.59,12 10.59,12 C10.59,12 5,17.59 5,17.59 C5,17.59 6.41,19 6.41,19 C6.41,19 11,14.41 11,14.41 C11,14.41 11,22 11,22 C11,22 12,22 12,22 C12,22 17.71,16.29 17.71,16.29 C17.71,16.29 13.41,12 13.41,12 C13.41,12 17.71,7.71 17.71,7.71c M13 5.83 C13,5.83 14.88,7.71 14.88,7.71 C14.88,7.71 13,9.59 13,9.59 C13,9.59 13,5.83 13,5.83c M14.88 16.29 C14.88,16.29 13,18.17 13,18.17 C13,18.17 13,14.41 13,14.41 C13,14.41 14.88,16.29 14.88,16.29c "/> + <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M5 10.5 C5.83,10.5 6.5,11.17 6.5,12 C6.5,12.83 5.83,13.5 5,13.5 C4.17,13.5 3.5,12.83 3.5,12 C3.5,11.17 4.17,10.5 5,10.5c "/> + <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M19 10.5 C19.83,10.5 20.5,11.17 20.5,12 C20.5,12.83 19.83,13.5 19,13.5 C18.17,13.5 17.5,12.83 17.5,12 C17.5,11.17 18.17,10.5 19,10.5c "/> + </group> + </group> + </group> + <group android:name="time_group"/> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_hotspot_transient_animation.xml b/core/res/res/drawable/ic_hotspot_transient_animation.xml new file mode 100644 index 000000000000..efbdfdbf6a75 --- /dev/null +++ b/core/res/res/drawable/ic_hotspot_transient_animation.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/ic_hotspot_transient_animation_drawable"> + <target android:name="_R_G_L_0_G_D_0_P_0" + android:animation="@anim/ic_hotspot_transient_animation_0"/> + <target android:name="_R_G_L_0_G_D_1_P_0" + android:animation="@anim/ic_hotspot_transient_animation_1"/> + <target android:name="_R_G_L_0_G_D_2_P_0" + android:animation="@anim/ic_hotspot_transient_animation_2"/> + <target android:name="time_group" + android:animation="@anim/ic_hotspot_transient_animation_3"/> +</animated-vector> diff --git a/core/res/res/drawable/ic_hotspot_transient_animation_drawable.xml b/core/res/res/drawable/ic_hotspot_transient_animation_drawable.xml new file mode 100644 index 000000000000..d81abbb8a0b8 --- /dev/null +++ b/core/res/res/drawable/ic_hotspot_transient_animation_drawable.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_0_G" android:translateX="-10" + android:translateY="-9"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M10 8 C8.9,8 8,8.9 8,10 C8,10.55 8.23,11.05 8.59,11.41 C8.95,11.77 9.45,12 10,12 C10.55,12 11.05,11.77 11.41,11.41 C11.77,11.05 12,10.55 12,10 C12,8.9 11.1,8 10,8c "/> + <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M10 4 C6.69,4 4,6.69 4,10 C4,11.66 4.68,13.15 5.76,14.24 C5.76,14.24 7.18,12.82 7.18,12.82 C6.45,12.1 6,11.11 6,10 C6,7.79 7.79,6 10,6 C12.21,6 14,7.79 14,10 C14,11.11 13.55,12.1 12.82,12.82 C12.82,12.82 14.24,14.24 14.24,14.24 C15.32,13.15 16,11.66 16,10 C16,6.69 13.31,4 10,4c "/> + <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M10 0 C4.48,0 0,4.48 0,10 C0,12.76 1.12,15.26 2.93,17.07 C2.93,17.07 4.34,15.66 4.34,15.66 C2.9,14.21 2,12.21 2,10 C2,5.58 5.58,2 10,2 C14.42,2 18,5.58 18,10 C18,12.21 17.1,14.2 15.65,15.65 C15.65,15.65 17.06,17.06 17.06,17.06 C18.88,15.26 20,12.76 20,10 C20,4.48 15.52,0 10,0c "/> + </group> + </group> + </group> + <group android:name="time_group"/> +</vector> diff --git a/core/res/res/drawable/ic_signal_wifi_transient_animation.xml b/core/res/res/drawable/ic_signal_wifi_transient_animation.xml new file mode 100644 index 000000000000..0219ae52396f --- /dev/null +++ b/core/res/res/drawable/ic_signal_wifi_transient_animation.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/ic_signal_wifi_transient_animation_drawable"> + <target android:name="_R_G_L_4_G_N_1_T_0" + android:animation="@anim/ic_signal_wifi_transient_animation_0" /> + <target android:name="_R_G_L_3_G_N_1_T_0" + android:animation="@anim/ic_signal_wifi_transient_animation_1" /> + <target android:name="_R_G_L_3_G_N_1_T_0" + android:animation="@anim/ic_signal_wifi_transient_animation_2" /> + <target android:name="_R_G_L_2_G_N_1_T_0" + android:animation="@anim/ic_signal_wifi_transient_animation_3" /> + <target android:name="_R_G_L_2_G_N_1_T_0" + android:animation="@anim/ic_signal_wifi_transient_animation_4" /> + <target android:name="_R_G_L_1_G_N_1_T_0" + android:animation="@anim/ic_signal_wifi_transient_animation_5" /> + <target android:name="_R_G_L_1_G_N_1_T_0" + android:animation="@anim/ic_signal_wifi_transient_animation_6" /> + <target android:name="_R_G_L_0_G_N_1_T_0" + android:animation="@anim/ic_signal_wifi_transient_animation_7" /> + <target android:name="time_group" + android:animation="@anim/ic_signal_wifi_transient_animation_8" /> +</animated-vector> diff --git a/core/res/res/drawable/ic_signal_wifi_transient_animation_drawable.xml b/core/res/res/drawable/ic_signal_wifi_transient_animation_drawable.xml new file mode 100644 index 000000000000..9cd4925d1f97 --- /dev/null +++ b/core/res/res/drawable/ic_signal_wifi_transient_animation_drawable.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" android:width="24dp" android:viewportHeight="24" android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_4_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_4_G" android:translateX="-12.25" + android:translateY="-10.25"> + <path android:name="_R_G_L_4_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 2.25 C15.67,2.25 18.98,3.52 21.55,5.78 C21.55,5.78 12.25,17.1 12.25,17.1 C12.25,17.1 2.95,5.78 2.95,5.78 C5.52,3.52 8.83,2.25 12.25,2.25c M12.25 0.25 C7.5,0.25 3.22,2.33 0.25,5.64 C0.25,5.64 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.64 24.25,5.64 C21.28,2.33 17,0.25 12.25,0.25c "/> + </group> + </group> + <group android:name="_R_G_L_3_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_3_G" android:translateX="-12.25" + android:translateY="-10.25"> + <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 2.25 C15.65,2.25 18.95,3.55 21.55,5.75 C21.55,5.75 12.25,17.05 12.25,17.05 C12.25,17.05 2.95,5.75 2.95,5.75 C5.55,3.55 8.85,2.25 12.25,2.25c M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/> + <path android:name="_R_G_L_3_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 11.25 C10.15,11.25 8.15,12.15 6.85,13.65 C6.85,13.65 12.25,20.25 12.25,20.25 C12.25,20.25 17.65,13.65 17.65,13.65 C16.35,12.15 14.35,11.25 12.25,11.25c "/> + </group> + </group> + <group android:name="_R_G_L_2_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_2_G" android:translateX="-12.25" + android:translateY="-10.25"> + <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 2.25 C15.65,2.25 18.95,3.55 21.55,5.75 C21.55,5.75 12.25,17.05 12.25,17.05 C12.25,17.05 2.95,5.75 2.95,5.75 C5.55,3.55 8.85,2.25 12.25,2.25c M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/> + <path android:name="_R_G_L_2_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 8.25 C9.45,8.25 6.75,9.45 5.05,11.45 C5.05,11.45 12.25,20.25 12.25,20.25 C12.25,20.25 19.45,11.45 19.45,11.45 C17.75,9.45 15.05,8.25 12.25,8.25c "/> + </group> + </group> + <group android:name="_R_G_L_1_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_1_G" android:translateX="-12.25" + android:translateY="-10.25"> + <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 2.25 C15.65,2.25 18.95,3.55 21.55,5.75 C21.55,5.75 12.25,17.05 12.25,17.05 C12.25,17.05 2.95,5.75 2.95,5.75 C5.55,3.55 8.85,2.25 12.25,2.25c M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/> + <path android:name="_R_G_L_1_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 5.25 C8.75,5.25 5.45,6.75 3.25,9.25 C3.25,9.25 12.25,20.25 12.25,20.25 C12.25,20.25 21.25,9.25 21.25,9.25 C19.05,6.75 15.75,5.25 12.25,5.25c "/> + </group> + </group> + <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_0_G" android:translateX="-12.25" + android:translateY="-10.25"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 2.25 C15.65,2.25 18.95,3.55 21.55,5.75 C21.55,5.75 12.25,17.05 12.25,17.05 C12.25,17.05 2.95,5.75 2.95,5.75 C5.55,3.55 8.85,2.25 12.25,2.25c M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/> + <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 5.25 C8.75,5.25 5.45,6.75 3.25,9.25 C3.25,9.25 12.25,20.25 12.25,20.25 C12.25,20.25 21.25,9.25 21.25,9.25 C19.05,6.75 15.75,5.25 12.25,5.25c "/> + <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/> + </group> + </group> + </group> + <group android:name="time_group"/> +</vector>
\ No newline at end of file diff --git a/core/res/res/interpolator/transient_interpolator.xml b/core/res/res/interpolator/transient_interpolator.xml new file mode 100644 index 000000000000..653a8cfdcdc0 --- /dev/null +++ b/core/res/res/interpolator/transient_interpolator.xml @@ -0,0 +1,17 @@ +<!-- + ~ Copyright (C) 2019 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 + --> +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
\ No newline at end of file diff --git a/core/res/res/layout/chooser_grid_preview_text.xml b/core/res/res/layout/chooser_grid_preview_text.xml index e889e8584fec..96a642c6d446 100644 --- a/core/res/res/layout/chooser_grid_preview_text.xml +++ b/core/res/res/layout/chooser_grid_preview_text.xml @@ -60,7 +60,7 @@ android:minWidth="48dp" android:minHeight="48dp" android:clickable="true" - android:background="?android:attr/selectableItemBackgroundBorderless"> + style="?attr/borderlessButtonStyle"> <ImageView android:layout_width="24dp" diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml index 256d94e5c330..7098c958ce36 100644 --- a/core/res/res/layout/resolve_grid_item.xml +++ b/core/res/res/layout/resolve_grid_item.xml @@ -23,7 +23,7 @@ android:minHeight="100dp" android:gravity="center" android:paddingTop="24dp" - android:paddingBottom="8dp" + android:paddingBottom="12dp" android:paddingLeft="12dp" android:paddingRight="12dp" android:focusable="true" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 04ccb74dae3c..f576feae0663 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1257,6 +1257,8 @@ <bool name="config_use_strict_phone_number_comparation">false</bool> + <bool name="config_use_strict_phone_number_comparation_for_russian">true</bool> + <!-- Display low battery warning when battery level dips to this value. Also, the battery stats are flushed to disk when we hit this level. --> <integer name="config_criticalBatteryWarningLevel">5</integer> @@ -4006,7 +4008,7 @@ <string name="config_batterymeterPerimeterPath" translatable="false"> M3.5,2 v0 H1.33 C0.6,2 0,2.6 0,3.33 V13v5.67 C0,19.4 0.6,20 1.33,20 h9.33 C11.4,20 12,19.4 12,18.67 V13V3.33 C12,2.6 11.4,2 10.67,2 H8.5 V0 H3.5 z M2,18v-7V4h8v9v5H2L2,18z </string> - + <string name="config_batterymeterErrorPerimeterPath" translatable="false">@string/config_batterymeterPerimeterPath</string> <string name="config_batterymeterFillMask" translatable="false"> M2,18 v-14 h8 v14 z </string> @@ -4057,4 +4059,38 @@ <!-- Which binder services to include in incident reports containing restricted images. --> <string-array name="config_restrictedImagesServices" translatable="false"/> + + <!-- Messages that should not be shown to the user during face auth enrollment. This should be + used to hide messages that may be too chatty or messages that the user can't do much about. + Entries are defined in android.hardware.biometrics.face@1.0 types.hal --> + <integer-array name="config_face_acquire_enroll_ignorelist" translatable="false" > + </integer-array> + <!-- Same as the above, but are defined by vendorCodes --> + <integer-array name="config_face_acquire_vendor_enroll_ignorelist" translatable="false" > + </integer-array> + + <!-- Messages that should not be shown to the user during face authentication, on keyguard. + This includes both lockscreen and bouncer. This should be used to hide messages that may be + too chatty or messages that the user can't do much about. Entries are defined in + android.hardware.biometrics.face@1.0 types.hal --> + <integer-array name="config_face_acquire_keyguard_ignorelist" translatable="false" > + </integer-array> + <!-- Same as the above, but are defined by vendorCodes --> + <integer-array name="config_face_acquire_vendor_keyguard_ignorelist" translatable="false" > + </integer-array> + + <!-- Messages that should not be shown to the user during face authentication, on + BiometricPrompt. This should be used to hide messages that may be too chatty or messages that + the user can't do much about. Entries are defined in + android.hardware.biometrics.face@1.0 types.hal --> + <integer-array name="config_face_acquire_biometricprompt_ignorelist" translatable="false" > + </integer-array> + <!-- Same as the above, but are defined by vendorCodes --> + <integer-array name="config_face_acquire_vendor_biometricprompt_ignorelist" translatable="false" > + </integer-array> + + <!-- The component name for the default profile supervisor, which can be set as a profile owner + even after user setup is complete. The defined component should be used for supervision purposes + only. The component must be part of a system app. --> + <string name="config_defaultSupervisionProfileOwnerComponent" translatable="false"></string> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 75fd3a0c04b7..dd0a6e605f60 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1545,9 +1545,9 @@ <!-- Message shown during face enrollment when the face is too similar to a previous acquisition [CHAR LIMIT=50] --> <string name="face_acquired_too_similar">Too similar, please change your pose.</string> <!-- Message shown during acqusition when the user's face is turned too far left or right [CHAR LIMIT=50] --> - <string name="face_acquired_pan_too_extreme">Please look more directly at the screen.</string> + <string name="face_acquired_pan_too_extreme">Turn your head a little less.</string> <!-- Message shown during acqusition when the user's face is tilted too high or too low [CHAR LIMIT=50] --> - <string name="face_acquired_tilt_too_extreme">Please look more directly at the screen.</string> + <string name="face_acquired_tilt_too_extreme">Turn your head a little less.</string> <!-- Message shown during acquisiton when the user's face is tilted too far left or right [CHAR LIMIT=50] --> <string name="face_acquired_roll_too_extreme">Please straighten your head vertically.</string> <!-- Message shown during acquisition when the user's face is obscured [CHAR LIMIT=50] --> diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml index b716ab985175..b7c86d266070 100644 --- a/core/res/res/values/styles_device_defaults.xml +++ b/core/res/res/values/styles_device_defaults.xml @@ -68,18 +68,27 @@ easier. <style name="Widget.DeviceDefault.HorizontalScrollView" parent="Widget.Material.HorizontalScrollView"/> <style name="Widget.DeviceDefault.Spinner" parent="Widget.Material.Spinner"/> <style name="Widget.DeviceDefault.CompoundButton.Star" parent="Widget.Material.CompoundButton.Star"/> - <style name="Widget.DeviceDefault.TabWidget" parent="Widget.Material.TabWidget"/> + <style name="Widget.DeviceDefault.TabWidget" parent="Widget.Material.TabWidget"> + <item name="textAppearance">@style/TextAppearance.DeviceDefault.Widget.TabWidget</item> + </style> <style name="Widget.DeviceDefault.WebTextView" parent="Widget.Material.WebTextView"/> <style name="Widget.DeviceDefault.WebView" parent="Widget.Material.WebView"/> - <style name="Widget.DeviceDefault.DropDownItem" parent="Widget.Material.DropDownItem"/> - <style name="Widget.DeviceDefault.DropDownItem.Spinner" parent="Widget.Material.DropDownItem.Spinner"/> - <style name="Widget.DeviceDefault.TextView.SpinnerItem" parent="Widget.Material.TextView.SpinnerItem"/> + <style name="Widget.DeviceDefault.DropDownItem" parent="Widget.Material.DropDownItem"> + <item name="textAppearance">@style/TextAppearance.DeviceDefault.Widget.DropDownItem</item> + </style> + <style name="Widget.DeviceDefault.DropDownItem.Spinner" parent="Widget.DeviceDefault.DropDownItem"/> + <style name="Widget.DeviceDefault.TextView.SpinnerItem" parent="Widget.Material.TextView.SpinnerItem"> + <item name="textAppearance">@style/TextAppearance.DeviceDefault.Widget.TextView.SpinnerItem</item> + </style> <style name="Widget.DeviceDefault.ListPopupWindow" parent="Widget.Material.ListPopupWindow"/> <style name="Widget.DeviceDefault.PopupMenu" parent="Widget.Material.PopupMenu"/> <style name="Widget.DeviceDefault.ActionButton" parent="Widget.Material.ActionButton"/> <style name="Widget.DeviceDefault.ActionButton.Overflow" parent="Widget.Material.ActionButton.Overflow"/> <style name="Widget.DeviceDefault.ActionButton.TextButton" parent="Widget.Material.ActionButton"/> - <style name="Widget.DeviceDefault.ActionMode" parent="Widget.Material.ActionMode"/> + <style name="Widget.DeviceDefault.ActionMode" parent="Widget.Material.ActionMode"> + <item name="titleTextStyle">@style/TextAppearance.DeviceDefault.Widget.ActionMode.Title</item> + <item name="subtitleTextStyle">@style/TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle</item> + </style> <style name="Widget.DeviceDefault.ActionButton.CloseMode" parent="Widget.Material.ActionButton.CloseMode"/> <style name="Widget.DeviceDefault.ActionBar" parent="Widget.Material.ActionBar"/> <style name="Widget.DeviceDefault.Button.Borderless" parent="Widget.Material.Button.Borderless"/> @@ -108,7 +117,9 @@ easier. <style name="Widget.DeviceDefault.AbsListView" parent="Widget.Material.AbsListView"/> <style name="Widget.DeviceDefault.Spinner.DropDown.ActionBar" parent="Widget.Material.Spinner.DropDown.ActionBar"/> <style name="Widget.DeviceDefault.PopupWindow.ActionMode" parent="Widget.Material.PopupWindow.ActionMode"/> - <style name="Widget.DeviceDefault.CompoundButton.Switch" parent="Widget.Material.CompoundButton.Switch"/> + <style name="Widget.DeviceDefault.CompoundButton.Switch" parent="Widget.Material.CompoundButton.Switch"> + <item name="switchTextAppearance">@style/TextAppearance.DeviceDefault.Widget.Switch</item> + </style> <style name="Widget.DeviceDefault.ExpandableListView.White" parent="Widget.Material.ExpandableListView.White"/> <style name="Widget.DeviceDefault.FastScroll" parent="Widget.Material.FastScroll"/> <!-- The FragmentBreadCrumbs widget is deprecated starting in API level 21 ({@link android.os.Build.VERSION_CODES#.L}). --> @@ -146,6 +157,7 @@ easier. <style name="Widget.DeviceDefault.TimePicker" parent="Widget.Material.TimePicker"/> <style name="Widget.DeviceDefault.Toolbar" parent="Widget.Material.Toolbar"> <item name="titleTextAppearance">@style/TextAppearance.DeviceDefault.Widget.Toolbar.Title</item> + <item name="subtitleTextAppearance">@style/TextAppearance.DeviceDefault.Widget.Toolbar.Subtitle</item> </style> <style name="Widget.DeviceDefault.Light" parent="Widget.Material.Light"/> @@ -186,12 +198,12 @@ easier. <style name="Widget.DeviceDefault.Light.HorizontalScrollView" parent="Widget.Material.Light.HorizontalScrollView"/> <style name="Widget.DeviceDefault.Light.Spinner" parent="Widget.Material.Light.Spinner"/> <style name="Widget.DeviceDefault.Light.CompoundButton.Star" parent="Widget.Material.Light.CompoundButton.Star"/> - <style name="Widget.DeviceDefault.Light.TabWidget" parent="Widget.Material.Light.TabWidget"/> + <style name="Widget.DeviceDefault.Light.TabWidget" parent="Widget.DeviceDefault.TabWidget"/> <style name="Widget.DeviceDefault.Light.WebTextView" parent="Widget.Material.Light.WebTextView"/> <style name="Widget.DeviceDefault.Light.WebView" parent="Widget.Material.Light.WebView"/> - <style name="Widget.DeviceDefault.Light.DropDownItem" parent="Widget.Material.Light.DropDownItem"/> - <style name="Widget.DeviceDefault.Light.DropDownItem.Spinner" parent="Widget.Material.Light.DropDownItem.Spinner"/> - <style name="Widget.DeviceDefault.Light.TextView.SpinnerItem" parent="Widget.Material.Light.TextView.SpinnerItem"/> + <style name="Widget.DeviceDefault.Light.DropDownItem" parent="Widget.DeviceDefault.DropDownItem"/> + <style name="Widget.DeviceDefault.Light.DropDownItem.Spinner" parent="Widget.DeviceDefault.DropDownItem.Spinner"/> + <style name="Widget.DeviceDefault.Light.TextView.SpinnerItem" parent="Widget.DeviceDefault.TextView.SpinnerItem"/> <style name="Widget.DeviceDefault.Light.ListPopupWindow" parent="Widget.Material.Light.ListPopupWindow"/> <style name="Widget.DeviceDefault.Light.PopupMenu" parent="Widget.Material.Light.PopupMenu"/> <style name="Widget.DeviceDefault.Light.Tab" parent="Widget.Material.Light.Tab"/> @@ -202,12 +214,10 @@ easier. <style name="Widget.DeviceDefault.Light.ActionMode" parent="Widget.Material.Light.ActionMode"/> <style name="Widget.DeviceDefault.Light.ActionButton.CloseMode" parent="Widget.Material.Light.ActionButton.CloseMode"/> <style name="Widget.DeviceDefault.Light.ActionBar" parent="Widget.Material.Light.ActionBar"/> - <style name="Widget.DeviceDefault.Light.ActionBar.TabView" parent="Widget.Material.Light.ActionBar.TabView"/> - <style name="Widget.DeviceDefault.Light.ActionBar.TabText" parent="Widget.Material.Light.ActionBar.TabText"/> - <style name="Widget.DeviceDefault.Light.ActionBar.TabBar" parent="Widget.Material.Light.ActionBar.TabBar"/> - <style name="Widget.DeviceDefault.Light.ActionBar.Solid" parent="Widget.Material.Light.ActionBar.Solid"> - <item name="titleTextStyle">@style/TextAppearance.DeviceDefault.Widget.ActionBar.Title</item> - </style> + <style name="Widget.DeviceDefault.Light.ActionBar.TabView" parent="Widget.DeviceDefault.ActionBar.TabView" /> + <style name="Widget.DeviceDefault.Light.ActionBar.TabText" parent="Widget.DeviceDefault.ActionBar.TabText" /> + <style name="Widget.DeviceDefault.Light.ActionBar.TabBar" parent="Widget.DeviceDefault.ActionBar.TabBar" /> + <style name="Widget.DeviceDefault.Light.ActionBar.Solid" parent="Widget.DeviceDefault.ActionBar.Solid" /> <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. --> <style name="Widget.DeviceDefault.Light.ActionBar.Solid.Inverse" parent="Widget.Holo.Light.ActionBar.Solid.Inverse"/> <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. --> @@ -307,6 +317,9 @@ easier. <style name="TextAppearance.DeviceDefault.Widget.EditText" parent="TextAppearance.Material.Widget.EditText"> <item name="fontFamily">@string/config_bodyFontFamily</item> </style> + <style name="TextAppearance.DeviceDefault.Widget.Switch" parent="TextAppearance.Material.Widget.Switch"> + <item name="fontFamily">@string/config_bodyFontFamilyMedium</item> + </style> <style name="TextAppearance.DeviceDefault.Widget.Button.Borderless.Colored" parent="TextAppearance.DeviceDefault.Widget.Button"> <item name="textColor">@color/btn_colored_borderless_text_material</item> </style> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index fbe340ed61d3..d178383973fd 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -313,6 +313,7 @@ <java-symbol type="bool" name="config_ui_enableFadingMarquee" /> <java-symbol type="bool" name="config_enableHapticTextHandle" /> <java-symbol type="bool" name="config_use_strict_phone_number_comparation" /> + <java-symbol type="bool" name="config_use_strict_phone_number_comparation_for_russian" /> <java-symbol type="bool" name="config_single_volume" /> <java-symbol type="bool" name="config_voice_capable" /> <java-symbol type="bool" name="config_requireCallCapableAccountForHandle" /> @@ -1406,6 +1407,9 @@ <java-symbol type="drawable" name="ic_wifi_signal_2" /> <java-symbol type="drawable" name="ic_wifi_signal_3" /> <java-symbol type="drawable" name="ic_wifi_signal_4" /> + <java-symbol type="drawable" name="ic_signal_wifi_transient_animation" /> + <java-symbol type="drawable" name="ic_hotspot_transient_animation" /> + <java-symbol type="drawable" name="ic_bluetooth_transient_animation" /> <java-symbol type="drawable" name="stat_notify_rssi_in_range" /> <java-symbol type="drawable" name="stat_sys_gps_on" /> <java-symbol type="drawable" name="stat_sys_tether_wifi" /> @@ -2570,6 +2574,13 @@ <java-symbol type="string" name="face_authenticated_no_confirmation_required" /> <java-symbol type="string" name="face_authenticated_confirmation_required" /> + <java-symbol type="array" name="config_face_acquire_enroll_ignorelist" /> + <java-symbol type="array" name="config_face_acquire_vendor_enroll_ignorelist" /> + <java-symbol type="array" name="config_face_acquire_keyguard_ignorelist" /> + <java-symbol type="array" name="config_face_acquire_vendor_keyguard_ignorelist" /> + <java-symbol type="array" name="config_face_acquire_biometricprompt_ignorelist" /> + <java-symbol type="array" name="config_face_acquire_vendor_biometricprompt_ignorelist" /> + <!-- Face config --> <java-symbol type="integer" name="config_faceMaxTemplatesPerUser" /> @@ -3240,6 +3251,7 @@ <java-symbol type="string" name="config_icon_mask" /> <java-symbol type="string" name="config_batterymeterPerimeterPath" /> + <java-symbol type="string" name="config_batterymeterErrorPerimeterPath" /> <java-symbol type="string" name="config_batterymeterFillMask" /> <java-symbol type="string" name="config_batterymeterBoltPath" /> <java-symbol type="string" name="config_batterymeterPowersavePath" /> @@ -3773,4 +3785,6 @@ <java-symbol type="dimen" name="chooser_direct_share_label_placeholder_max_width" /> <java-symbol type="layout" name="chooser_az_label_row" /> <java-symbol type="string" name="chooser_all_apps_button_label" /> + + <java-symbol type="string" name="config_defaultSupervisionProfileOwnerComponent" /> </resources> diff --git a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java index 2f17b32370f4..a8ca6f048a11 100644 --- a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java +++ b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java @@ -16,7 +16,10 @@ package android.view.autofill; +import static android.view.autofill.AutofillId.NO_SESSION; + import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static org.testng.Assert.assertThrows; @@ -33,20 +36,10 @@ public class AutofillIdTest { @Test public void testNonVirtual() { final AutofillId id = new AutofillId(42); - assertThat(id.getViewId()).isEqualTo(42); - assertThat(id.isNonVirtual()).isTrue(); - assertThat(id.isVirtualInt()).isFalse(); - assertThat(id.isVirtualLong()).isFalse(); - assertThat(id.getVirtualChildIntId()).isEqualTo(View.NO_ID); - assertThat(id.getVirtualChildLongId()).isEqualTo(View.NO_ID); + assertNonVirtual(id, 42, NO_SESSION); final AutofillId clone = cloneThroughParcel(id); - assertThat(clone.getViewId()).isEqualTo(42); - assertThat(clone.isNonVirtual()).isTrue(); - assertThat(clone.isVirtualInt()).isFalse(); - assertThat(clone.isVirtualLong()).isFalse(); - assertThat(clone.getVirtualChildIntId()).isEqualTo(View.NO_ID); - assertThat(clone.getVirtualChildLongId()).isEqualTo(View.NO_ID); + assertNonVirtual(clone, 42, NO_SESSION); } @Test @@ -124,49 +117,174 @@ public class AutofillIdTest { } @Test - public void testEqualsHashCode() { - final AutofillId realId = new AutofillId(42); - final AutofillId realIdSame = new AutofillId(42); - assertThat(realId).isEqualTo(realIdSame); - assertThat(realIdSame).isEqualTo(realId); - assertThat(realId.hashCode()).isEqualTo(realIdSame.hashCode()); - - final AutofillId realIdDifferent = new AutofillId(108); - assertThat(realId).isNotEqualTo(realIdDifferent); - assertThat(realIdDifferent).isNotEqualTo(realId); - - final AutofillId virtualId = new AutofillId(42, 1); - final AutofillId virtualIdSame = new AutofillId(42, 1); - assertThat(virtualId).isEqualTo(virtualIdSame); - assertThat(virtualIdSame).isEqualTo(virtualId); - assertThat(virtualId.hashCode()).isEqualTo(virtualIdSame.hashCode()); - assertThat(virtualId).isNotEqualTo(realId); - assertThat(realId).isNotEqualTo(virtualId); - - final AutofillId virtualIdDifferentChild = new AutofillId(42, 2); - assertThat(virtualIdDifferentChild).isNotEqualTo(virtualId); - assertThat(virtualId).isNotEqualTo(virtualIdDifferentChild); - assertThat(virtualIdDifferentChild).isNotEqualTo(realId); - assertThat(realId).isNotEqualTo(virtualIdDifferentChild); - - final AutofillId virtualIdDifferentParent = new AutofillId(108, 1); - assertThat(virtualIdDifferentParent).isNotEqualTo(virtualId); - assertThat(virtualId).isNotEqualTo(virtualIdDifferentParent); - assertThat(virtualIdDifferentParent).isNotEqualTo(virtualIdDifferentChild); - assertThat(virtualIdDifferentChild).isNotEqualTo(virtualIdDifferentParent); - - final AutofillId virtualIdDifferentSession = new AutofillId(new AutofillId(42), 1L, 108); - assertThat(virtualIdDifferentSession).isNotEqualTo(virtualId); - assertThat(virtualId).isNotEqualTo(virtualIdDifferentSession); - assertThat(virtualIdDifferentSession).isNotEqualTo(realId); - assertThat(realId).isNotEqualTo(virtualIdDifferentSession); - - final AutofillId sameVirtualIdDifferentSession = - new AutofillId(new AutofillId(42), 1L, 108); - assertThat(sameVirtualIdDifferentSession).isEqualTo(virtualIdDifferentSession); - assertThat(virtualIdDifferentSession).isEqualTo(sameVirtualIdDifferentSession); - assertThat(sameVirtualIdDifferentSession.hashCode()) - .isEqualTo(virtualIdDifferentSession.hashCode()); + public void testFactoryMethod_withoutSession() { + final AutofillId id = new AutofillId(42); + id.setSessionId(108); + assertNonVirtual(id, 42, 108); + final AutofillId idWithoutSession = AutofillId.withoutSession(id); + assertNonVirtual(idWithoutSession, 42, NO_SESSION); + } + + @Test + public void testSetResetSession() { + final AutofillId id = new AutofillId(42); + assertNonVirtual(id, 42, NO_SESSION); + id.setSessionId(108); + assertNonVirtual(id, 42, 108); + + final AutofillId clone1 = cloneThroughParcel(id); + assertNonVirtual(clone1, 42, 108); + + id.resetSessionId(); + assertThat(id.getSessionId()).isEqualTo(NO_SESSION); + final AutofillId clone2 = cloneThroughParcel(id); + assertNonVirtual(clone2, 42, NO_SESSION); + } + + @Test + public void testEqualsHashCode_nonVirtual_same() { + final AutofillId id = new AutofillId(42); + final AutofillId sameId = new AutofillId(42); + + assertThat(id).isEqualTo(sameId); + assertThat(sameId).isEqualTo(id); + assertEqualsIgnoreSession(id, sameId); + assertEqualsIgnoreSession(sameId, id); + assertThat(id.hashCode()).isEqualTo(sameId.hashCode()); + } + + @Test + public void testEqualsHashCode_nonVirtual_other() { + final AutofillId id = new AutofillId(42); + final AutofillId otherId = new AutofillId(108); + + assertThat(id).isNotEqualTo(otherId); + assertThat(otherId).isNotEqualTo(id); + assertNotEqualsIgnoreSession(id, otherId); + assertNotEqualsIgnoreSession(otherId, id); + assertThat(id.hashCode()).isNotEqualTo(otherId.hashCode()); + } + + @Test + public void testEqualsHashCode_virtual_same() { + final AutofillId id = new AutofillId(42); + final AutofillId virtual = new AutofillId(42, 1); + final AutofillId sameVirtual = new AutofillId(42, 1); + + assertThat(virtual).isEqualTo(sameVirtual); + assertThat(sameVirtual).isEqualTo(virtual); + assertEqualsIgnoreSession(virtual, sameVirtual); + assertEqualsIgnoreSession(sameVirtual, virtual); + assertThat(virtual.hashCode()).isEqualTo(sameVirtual.hashCode()); + assertThat(virtual).isNotEqualTo(id); + assertThat(id).isNotEqualTo(virtual); + assertNotEqualsIgnoreSession(id, virtual); + assertNotEqualsIgnoreSession(virtual, id); + } + + @Test + public void testEqualsHashCode_virtual_otherChild() { + final AutofillId id = new AutofillId(42); + final AutofillId virtual = new AutofillId(42, 1); + final AutofillId virtualOtherChild = new AutofillId(42, 2); + + assertThat(virtualOtherChild).isNotEqualTo(virtual); + assertThat(virtual).isNotEqualTo(virtualOtherChild); + assertNotEqualsIgnoreSession(virtualOtherChild, virtual); + assertNotEqualsIgnoreSession(virtual, virtualOtherChild); + assertThat(virtualOtherChild).isNotEqualTo(id); + assertThat(id).isNotEqualTo(virtualOtherChild); + assertNotEqualsIgnoreSession(virtualOtherChild, id); + assertNotEqualsIgnoreSession(id, virtualOtherChild); + } + + @Test + public void testEqualsHashCode_virtual_otherParent() { + final AutofillId virtual = new AutofillId(42, 1); + final AutofillId virtualOtherParent = new AutofillId(108, 1); + final AutofillId virtualOtherChild = new AutofillId(42, 2); + + assertThat(virtualOtherParent).isNotEqualTo(virtual); + assertThat(virtual).isNotEqualTo(virtualOtherParent); + assertNotEqualsIgnoreSession(virtualOtherParent, virtual); + assertNotEqualsIgnoreSession(virtual, virtualOtherParent); + assertThat(virtualOtherParent).isNotEqualTo(virtualOtherChild); + assertThat(virtualOtherChild).isNotEqualTo(virtualOtherParent); + assertNotEqualsIgnoreSession(virtualOtherParent, virtualOtherChild); + assertNotEqualsIgnoreSession(virtualOtherChild, virtualOtherParent); + } + + @Test + public void testEqualsHashCode_virtual_otherSession() { + final AutofillId virtual = new AutofillId(42, 1); + final AutofillId virtualOtherSession = new AutofillId(42, 1); + virtualOtherSession.setSessionId(666); + + assertThat(virtualOtherSession).isNotEqualTo(virtual); + assertThat(virtual).isNotEqualTo(virtualOtherSession); + assertEqualsIgnoreSession(virtualOtherSession, virtual); + assertEqualsIgnoreSession(virtual, virtualOtherSession); + } + + @Test + public void testEqualsHashCode_virtual_longId_same() { + final AutofillId hostId = new AutofillId(42); + final AutofillId virtual = new AutofillId(hostId, 1L, 108); + final AutofillId sameVirtual = new AutofillId(hostId, 1L, 108); + + assertThat(sameVirtual).isEqualTo(virtual); + assertThat(virtual).isEqualTo(sameVirtual); + assertEqualsIgnoreSession(sameVirtual, virtual); + assertEqualsIgnoreSession(virtual, sameVirtual); + assertThat(sameVirtual).isNotEqualTo(hostId); + assertThat(hostId).isNotEqualTo(sameVirtual); + assertNotEqualsIgnoreSession(sameVirtual, hostId); + assertNotEqualsIgnoreSession(hostId, sameVirtual); + } + + @Test + public void testEqualsHashCode_virtual_longId_otherChild() { + final AutofillId hostId = new AutofillId(42); + final AutofillId virtual = new AutofillId(hostId, 1L, 108); + final AutofillId virtualOtherChild = new AutofillId(hostId, 2L, 108); + + assertThat(virtualOtherChild).isNotEqualTo(virtual); + assertThat(virtual).isNotEqualTo(virtualOtherChild); + assertNotEqualsIgnoreSession(virtualOtherChild, virtual); + assertNotEqualsIgnoreSession(virtual, virtualOtherChild); + assertThat(virtualOtherChild).isNotEqualTo(hostId); + assertThat(hostId).isNotEqualTo(virtualOtherChild); + assertNotEqualsIgnoreSession(virtualOtherChild, hostId); + assertNotEqualsIgnoreSession(hostId, virtualOtherChild); + } + + @Test + public void testEqualsHashCode_virtual_longId_otherParent() { + final AutofillId hostId = new AutofillId(42); + final AutofillId virtual = new AutofillId(hostId, 1L, 108); + final AutofillId virtualOtherParent = new AutofillId(new AutofillId(666), 1L, 108); + final AutofillId virtualOtherChild = new AutofillId(hostId, 2L, 108); + + assertThat(virtualOtherParent).isNotEqualTo(virtual); + assertThat(virtual).isNotEqualTo(virtualOtherParent); + assertNotEqualsIgnoreSession(virtualOtherParent, virtual); + assertNotEqualsIgnoreSession(virtual, virtualOtherParent); + assertThat(virtualOtherParent).isNotEqualTo(virtualOtherChild); + assertThat(virtualOtherChild).isNotEqualTo(virtualOtherParent); + assertNotEqualsIgnoreSession(virtualOtherParent, virtualOtherChild); + assertNotEqualsIgnoreSession(virtualOtherChild, virtualOtherParent); + } + + @Test + public void testEqualsHashCode_virtual_longId_otherSession() { + final AutofillId hostId = new AutofillId(42); + final AutofillId virtual = new AutofillId(hostId, 1L, 108); + final AutofillId virtualOtherSession = new AutofillId(hostId, 1L, 666); + + assertThat(virtualOtherSession).isNotEqualTo(virtual); + assertThat(virtual).isNotEqualTo(virtualOtherSession); + assertEqualsIgnoreSession(virtualOtherSession, virtual); + assertEqualsIgnoreSession(virtual, virtualOtherSession); } private AutofillId cloneThroughParcel(AutofillId id) { @@ -186,4 +304,28 @@ public class AutofillIdTest { parcel.recycle(); } } + + private void assertEqualsIgnoreSession(AutofillId id1, AutofillId id2) { + assertWithMessage("id1 is null").that(id1).isNotNull(); + assertWithMessage("id2 is null").that(id2).isNotNull(); + assertWithMessage("%s is not equal to %s", id1, id2).that(id1.equalsIgnoreSession(id2)) + .isTrue(); + } + + private void assertNotEqualsIgnoreSession(AutofillId id1, AutofillId id2) { + assertWithMessage("id1 is null").that(id1).isNotNull(); + assertWithMessage("id2 is null").that(id2).isNotNull(); + assertWithMessage("%s is not equal to %s", id1, id2).that(id1.equalsIgnoreSession(id2)) + .isFalse(); + } + + private void assertNonVirtual(AutofillId id, int expectedId, int expectSessionId) { + assertThat(id.getViewId()).isEqualTo(expectedId); + assertThat(id.isNonVirtual()).isTrue(); + assertThat(id.isVirtualInt()).isFalse(); + assertThat(id.isVirtualLong()).isFalse(); + assertThat(id.getVirtualChildIntId()).isEqualTo(View.NO_ID); + assertThat(id.getVirtualChildLongId()).isEqualTo(View.NO_ID); + assertThat(id.getSessionId()).isEqualTo(expectSessionId); + } } diff --git a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java index ace6611a52c5..833530d76029 100644 --- a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java @@ -16,19 +16,9 @@ package android.view.inputmethod; -import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION; -import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION; -import static android.view.inputmethod.CursorAnchorInfo.FLAG_IS_RTL; - import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import android.graphics.Matrix; -import android.graphics.RectF; -import android.os.Parcel; -import android.text.TextUtils; import android.view.inputmethod.CursorAnchorInfo.Builder; import androidx.test.filters.SmallTest; @@ -37,437 +27,38 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.Objects; - @SmallTest @RunWith(AndroidJUnit4.class) public class CursorAnchorInfoTest { - private static final float EPSILON = 0.0000001f; - - private static final RectF[] MANY_BOUNDS = new RectF[] { - new RectF(101.0f, 201.0f, 301.0f, 401.0f), - new RectF(102.0f, 202.0f, 302.0f, 402.0f), - new RectF(103.0f, 203.0f, 303.0f, 403.0f), - new RectF(104.0f, 204.0f, 304.0f, 404.0f), - new RectF(105.0f, 205.0f, 305.0f, 405.0f), - new RectF(106.0f, 206.0f, 306.0f, 406.0f), - new RectF(107.0f, 207.0f, 307.0f, 407.0f), - new RectF(108.0f, 208.0f, 308.0f, 408.0f), - new RectF(109.0f, 209.0f, 309.0f, 409.0f), - new RectF(110.0f, 210.0f, 310.0f, 410.0f), - new RectF(111.0f, 211.0f, 311.0f, 411.0f), - new RectF(112.0f, 212.0f, 312.0f, 412.0f), - new RectF(113.0f, 213.0f, 313.0f, 413.0f), - new RectF(114.0f, 214.0f, 314.0f, 414.0f), - new RectF(115.0f, 215.0f, 315.0f, 415.0f), - new RectF(116.0f, 216.0f, 316.0f, 416.0f), - new RectF(117.0f, 217.0f, 317.0f, 417.0f), - new RectF(118.0f, 218.0f, 318.0f, 418.0f), - new RectF(119.0f, 219.0f, 319.0f, 419.0f), - }; - private static final int[] MANY_FLAGS_ARRAY = new int[] { - FLAG_HAS_INVISIBLE_REGION, - FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION, - FLAG_HAS_VISIBLE_REGION, - FLAG_HAS_VISIBLE_REGION, - FLAG_HAS_VISIBLE_REGION, - FLAG_HAS_VISIBLE_REGION, - FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL, - FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL, - FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL, - FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL, - FLAG_HAS_VISIBLE_REGION, - FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL, - FLAG_HAS_VISIBLE_REGION, - FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL, - FLAG_HAS_VISIBLE_REGION, - FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL, - FLAG_HAS_VISIBLE_REGION, - FLAG_HAS_INVISIBLE_REGION, - FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL, - }; - @Test - public void testBuilder() throws Exception { - final int SELECTION_START = 30; - final int SELECTION_END = 40; - final int COMPOSING_TEXT_START = 32; - final String COMPOSING_TEXT = "test"; - final int INSERTION_MARKER_FLAGS = - FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL; - final float INSERTION_MARKER_HORIZONTAL = 10.5f; - final float INSERTION_MARKER_TOP = 100.1f; - final float INSERTION_MARKER_BASELINE = 110.4f; - final float INSERTION_MARKER_BOTOM = 111.0f; - - Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX); - TRANSFORM_MATRIX.setScale(10.0f, 20.0f); - + public void testCreateForAdditionalParentMatrix() { + final Matrix originalMatrix = new Matrix(); + originalMatrix.setTranslate(10.0f, 20.0f); final Builder builder = new Builder(); - builder.setSelectionRange(SELECTION_START, SELECTION_END) - .setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT) - .setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP, - INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM, INSERTION_MARKER_FLAGS) - .setMatrix(TRANSFORM_MATRIX); - for (int i = 0; i < MANY_BOUNDS.length; i++) { - final RectF bounds = MANY_BOUNDS[i]; - final int flags = MANY_FLAGS_ARRAY[i]; - builder.addCharacterBounds(i, bounds.left, bounds.top, bounds.right, bounds.bottom, - flags); - } + builder.setMatrix(originalMatrix); - final CursorAnchorInfo info = builder.build(); - assertEquals(SELECTION_START, info.getSelectionStart()); - assertEquals(SELECTION_END, info.getSelectionEnd()); - assertEquals(COMPOSING_TEXT_START, info.getComposingTextStart()); - assertTrue(TextUtils.equals(COMPOSING_TEXT, info.getComposingText())); - assertEquals(INSERTION_MARKER_FLAGS, info.getInsertionMarkerFlags()); - assertEquals(INSERTION_MARKER_HORIZONTAL, info.getInsertionMarkerHorizontal(), EPSILON); - assertEquals(INSERTION_MARKER_TOP, info.getInsertionMarkerTop(), EPSILON); - assertEquals(INSERTION_MARKER_BASELINE, info.getInsertionMarkerBaseline(), EPSILON); - assertEquals(INSERTION_MARKER_BOTOM, info.getInsertionMarkerBottom(), EPSILON); - assertEquals(TRANSFORM_MATRIX, info.getMatrix()); - for (int i = 0; i < MANY_BOUNDS.length; i++) { - final RectF expectedBounds = MANY_BOUNDS[i]; - assertEquals(expectedBounds, info.getCharacterBounds(i)); - } - assertNull(info.getCharacterBounds(-1)); - assertNull(info.getCharacterBounds(MANY_BOUNDS.length + 1)); - for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) { - final int expectedFlags = MANY_FLAGS_ARRAY[i]; - assertEquals(expectedFlags, info.getCharacterBoundsFlags(i)); - } - assertEquals(0, info.getCharacterBoundsFlags(-1)); - assertEquals(0, info.getCharacterBoundsFlags(MANY_BOUNDS.length + 1)); + final CursorAnchorInfo originalInstance = builder.build(); - // Make sure that the builder can reproduce the same object. - final CursorAnchorInfo info2 = builder.build(); - assertEquals(SELECTION_START, info2.getSelectionStart()); - assertEquals(SELECTION_END, info2.getSelectionEnd()); - assertEquals(COMPOSING_TEXT_START, info2.getComposingTextStart()); - assertTrue(TextUtils.equals(COMPOSING_TEXT, info2.getComposingText())); - assertEquals(INSERTION_MARKER_FLAGS, info2.getInsertionMarkerFlags()); - assertEquals(INSERTION_MARKER_HORIZONTAL, info2.getInsertionMarkerHorizontal(), EPSILON); - assertEquals(INSERTION_MARKER_TOP, info2.getInsertionMarkerTop(), EPSILON); - assertEquals(INSERTION_MARKER_BASELINE, info2.getInsertionMarkerBaseline(), EPSILON); - assertEquals(INSERTION_MARKER_BOTOM, info2.getInsertionMarkerBottom(), EPSILON); - assertEquals(TRANSFORM_MATRIX, info2.getMatrix()); - for (int i = 0; i < MANY_BOUNDS.length; i++) { - final RectF expectedBounds = MANY_BOUNDS[i]; - assertEquals(expectedBounds, info2.getCharacterBounds(i)); - } - assertNull(info2.getCharacterBounds(-1)); - assertNull(info2.getCharacterBounds(MANY_BOUNDS.length + 1)); - for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) { - final int expectedFlags = MANY_FLAGS_ARRAY[i]; - assertEquals(expectedFlags, info2.getCharacterBoundsFlags(i)); - } - assertEquals(0, info2.getCharacterBoundsFlags(-1)); - assertEquals(0, info2.getCharacterBoundsFlags(MANY_BOUNDS.length + 1)); - assertEquals(info, info2); - assertEquals(info.hashCode(), info2.hashCode()); + assertEquals(originalMatrix, originalInstance.getMatrix()); - // Make sure that object can be marshaled via {@link Parsel}. - final CursorAnchorInfo info3 = cloneViaParcel(info2); - assertEquals(SELECTION_START, info3.getSelectionStart()); - assertEquals(SELECTION_END, info3.getSelectionEnd()); - assertEquals(COMPOSING_TEXT_START, info3.getComposingTextStart()); - assertTrue(TextUtils.equals(COMPOSING_TEXT, info3.getComposingText())); - assertEquals(INSERTION_MARKER_FLAGS, info3.getInsertionMarkerFlags()); - assertEquals(INSERTION_MARKER_HORIZONTAL, info3.getInsertionMarkerHorizontal(), EPSILON); - assertEquals(INSERTION_MARKER_TOP, info3.getInsertionMarkerTop(), EPSILON); - assertEquals(INSERTION_MARKER_BASELINE, info3.getInsertionMarkerBaseline(), EPSILON); - assertEquals(INSERTION_MARKER_BOTOM, info3.getInsertionMarkerBottom(), EPSILON); - assertEquals(TRANSFORM_MATRIX, info3.getMatrix()); - for (int i = 0; i < MANY_BOUNDS.length; i++) { - final RectF expectedBounds = MANY_BOUNDS[i]; - assertEquals(expectedBounds, info3.getCharacterBounds(i)); - } - assertNull(info3.getCharacterBounds(-1)); - assertNull(info3.getCharacterBounds(MANY_BOUNDS.length + 1)); - for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) { - final int expectedFlags = MANY_FLAGS_ARRAY[i]; - assertEquals(expectedFlags, info3.getCharacterBoundsFlags(i)); - } - assertEquals(0, info3.getCharacterBoundsFlags(-1)); - assertEquals(0, info3.getCharacterBoundsFlags(MANY_BOUNDS.length + 1)); - assertEquals(info.hashCode(), info3.hashCode()); + final Matrix additionalParentMatrix = new Matrix(); + additionalParentMatrix.setTranslate(1.0f, 2.0f); - builder.reset(); - final CursorAnchorInfo uninitializedInfo = builder.build(); - assertEquals(-1, uninitializedInfo.getSelectionStart()); - assertEquals(-1, uninitializedInfo.getSelectionEnd()); - assertEquals(-1, uninitializedInfo.getComposingTextStart()); - assertNull(uninitializedInfo.getComposingText()); - assertEquals(0, uninitializedInfo.getInsertionMarkerFlags()); - assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal(), EPSILON); - assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop(), EPSILON); - assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline(), EPSILON); - assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom(), EPSILON); - assertEquals(Matrix.IDENTITY_MATRIX, uninitializedInfo.getMatrix()); - } - - private static void assertNotEquals(final CursorAnchorInfo reference, - final CursorAnchorInfo actual) { - assertFalse(Objects.equals(reference, actual)); - } + final Matrix newMatrix = new Matrix(originalMatrix); + newMatrix.postConcat(additionalParentMatrix); - @Test - public void testEquality() throws Exception { - final Matrix MATRIX1 = new Matrix(); - MATRIX1.setTranslate(10.0f, 20.0f); - final Matrix MATRIX2 = new Matrix(); - MATRIX2.setTranslate(110.0f, 120.0f); - final Matrix NAN_MATRIX = new Matrix(); - NAN_MATRIX.setValues(new float[]{ - Float.NaN, Float.NaN, Float.NaN, - Float.NaN, Float.NaN, Float.NaN, - Float.NaN, Float.NaN, Float.NaN}); - final int SELECTION_START1 = 2; - final int SELECTION_END1 = 7; - final String COMPOSING_TEXT1 = "0123456789"; - final int COMPOSING_TEXT_START1 = 0; - final int INSERTION_MARKER_FLAGS1 = FLAG_HAS_VISIBLE_REGION; - final float INSERTION_MARKER_HORIZONTAL1 = 10.5f; - final float INSERTION_MARKER_TOP1 = 100.1f; - final float INSERTION_MARKER_BASELINE1 = 110.4f; - final float INSERTION_MARKER_BOTOM1 = 111.0f; - final int SELECTION_START2 = 4; - final int SELECTION_END2 = 8; - final String COMPOSING_TEXT2 = "9876543210"; - final int COMPOSING_TEXT_START2 = 3; - final int INSERTION_MARKER_FLAGS2 = - FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL; - final float INSERTION_MARKER_HORIZONTAL2 = 14.5f; - final float INSERTION_MARKER_TOP2 = 200.1f; - final float INSERTION_MARKER_BASELINE2 = 210.4f; - final float INSERTION_MARKER_BOTOM2 = 211.0f; - - // Default instance should be equal. - assertEquals(new Builder().build(), new Builder().build()); - - assertEquals( - new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(), - new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build()); - assertNotEquals( - new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(), - new Builder().setSelectionRange(SELECTION_START1, SELECTION_END2).build()); - assertNotEquals( - new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(), - new Builder().setSelectionRange(SELECTION_START2, SELECTION_END1).build()); - assertNotEquals( - new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(), - new Builder().setSelectionRange(SELECTION_START2, SELECTION_END2).build()); - assertEquals( - new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(), - new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build()); - assertNotEquals( - new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(), - new Builder().setComposingText(COMPOSING_TEXT_START2, COMPOSING_TEXT1).build()); - assertNotEquals( - new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(), - new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT2).build()); - assertNotEquals( - new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(), - new Builder().setComposingText(COMPOSING_TEXT_START2, COMPOSING_TEXT2).build()); - - // For insertion marker locations, {@link Float#NaN} is treated as if it was a number. - assertEquals( - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - Float.NaN, Float.NaN, Float.NaN, Float.NaN, - INSERTION_MARKER_FLAGS1).build(), - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - Float.NaN, Float.NaN, Float.NaN, Float.NaN, - INSERTION_MARKER_FLAGS1).build()); - - // Check Matrix. - assertEquals( - new Builder().setMatrix(MATRIX1).build(), - new Builder().setMatrix(MATRIX1).build()); - assertNotEquals( - new Builder().setMatrix(MATRIX1).build(), - new Builder().setMatrix(MATRIX2).build()); - assertNotEquals( - new Builder().setMatrix(MATRIX1).build(), - new Builder().setMatrix(NAN_MATRIX).build()); - // Unlike insertion marker locations, {@link Float#NaN} in the matrix is treated as just a - // NaN as usual (NaN == NaN -> false). - assertNotEquals( - new Builder().setMatrix(NAN_MATRIX).build(), - new Builder().setMatrix(NAN_MATRIX).build()); - - assertEquals( - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1, - INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1, - INSERTION_MARKER_FLAGS1).build(), - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1, - INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1, - INSERTION_MARKER_FLAGS1).build()); - assertNotEquals( - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - Float.NaN, INSERTION_MARKER_TOP1, - INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1, - INSERTION_MARKER_FLAGS1).build(), - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1, - INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1, - INSERTION_MARKER_FLAGS1).build()); - assertNotEquals( - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1, - INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1, - INSERTION_MARKER_FLAGS1).build(), - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - INSERTION_MARKER_HORIZONTAL2, INSERTION_MARKER_TOP1, - INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1, - INSERTION_MARKER_FLAGS1).build()); - assertNotEquals( - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1, - INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1, - INSERTION_MARKER_FLAGS1).build(), - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP2, - INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1, - INSERTION_MARKER_FLAGS1).build()); - assertNotEquals( - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1, - INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1, - INSERTION_MARKER_FLAGS1).build(), - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1, - INSERTION_MARKER_BASELINE2, INSERTION_MARKER_BOTOM1, - INSERTION_MARKER_FLAGS1).build()); - assertNotEquals( - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1, - INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1, - INSERTION_MARKER_FLAGS1).build(), - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - INSERTION_MARKER_HORIZONTAL2, INSERTION_MARKER_TOP1, - INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1, - INSERTION_MARKER_FLAGS1).build()); - assertNotEquals( - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1, - INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1, - INSERTION_MARKER_FLAGS1).build(), - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1, - INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM2, - INSERTION_MARKER_FLAGS1).build()); - assertNotEquals( - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1, - INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1, - INSERTION_MARKER_FLAGS1).build(), - new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation( - INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1, - INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1, - INSERTION_MARKER_FLAGS2).build()); - } - - @Test - public void testMatrixIsCopied() throws Exception { - final Matrix MATRIX1 = new Matrix(); - MATRIX1.setTranslate(10.0f, 20.0f); - final Matrix MATRIX2 = new Matrix(); - MATRIX2.setTranslate(110.0f, 120.0f); - final Matrix MATRIX3 = new Matrix(); - MATRIX3.setTranslate(210.0f, 220.0f); - final Matrix matrix = new Matrix(); - final Builder builder = new Builder(); - - matrix.set(MATRIX1); - builder.setMatrix(matrix); - matrix.postRotate(90.0f); - - final CursorAnchorInfo firstInstance = builder.build(); - assertEquals(MATRIX1, firstInstance.getMatrix()); - matrix.set(MATRIX2); - builder.setMatrix(matrix); - final CursorAnchorInfo secondInstance = builder.build(); - assertEquals(MATRIX1, firstInstance.getMatrix()); - assertEquals(MATRIX2, secondInstance.getMatrix()); - - matrix.set(MATRIX3); - assertEquals(MATRIX1, firstInstance.getMatrix()); - assertEquals(MATRIX2, secondInstance.getMatrix()); - } - - @Test - public void testMatrixIsRequired() throws Exception { - final int SELECTION_START = 30; - final int SELECTION_END = 40; - final int COMPOSING_TEXT_START = 32; - final String COMPOSING_TEXT = "test"; - final int INSERTION_MARKER_FLAGS = FLAG_HAS_VISIBLE_REGION; - final float INSERTION_MARKER_HORIZONTAL = 10.5f; - final float INSERTION_MARKER_TOP = 100.1f; - final float INSERTION_MARKER_BASELINE = 110.4f; - final float INSERTION_MARKER_BOTOM = 111.0f; - Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX); - TRANSFORM_MATRIX.setScale(10.0f, 20.0f); - - final Builder builder = new Builder(); - // Check twice to make sure if Builder#reset() works as expected. - for (int repeatCount = 0; repeatCount < 2; ++repeatCount) { - builder.setSelectionRange(SELECTION_START, SELECTION_END) - .setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT); - try { - // Should succeed as coordinate transformation matrix is not required if no - // positional information is specified. - builder.build(); - } catch (IllegalArgumentException ex) { - assertTrue(false); - } - - builder.setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP, - INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM, INSERTION_MARKER_FLAGS); - try { - // Coordinate transformation matrix is required if no positional information is - // specified. - builder.build(); - assertTrue(false); - } catch (IllegalArgumentException ex) { - } - - builder.setMatrix(TRANSFORM_MATRIX); - try { - // Should succeed as coordinate transformation matrix is required. - builder.build(); - } catch (IllegalArgumentException ex) { - assertTrue(false); - } - - builder.reset(); - } - } - - @Test - public void testBuilderAddCharacterBounds() throws Exception { - // A negative index should be rejected. - try { - new Builder().addCharacterBounds(-1, 0.0f, 0.0f, 0.0f, 0.0f, FLAG_HAS_VISIBLE_REGION); - assertTrue(false); - } catch (IllegalArgumentException ex) { - } - } - - private static CursorAnchorInfo cloneViaParcel(final CursorAnchorInfo src) { - Parcel parcel = null; - try { - parcel = Parcel.obtain(); - src.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - return new CursorAnchorInfo(parcel); - } finally { - if (parcel != null) { - parcel.recycle(); - } - } + builder.reset(); + builder.setMatrix(newMatrix); + // An instance created by the standard Builder class. + final CursorAnchorInfo newInstanceByBuilder = builder.build(); + + // An instance created by an @hide method. + final CursorAnchorInfo newInstanceByMethod = + CursorAnchorInfo.createForAdditionalParentMatrix( + originalInstance, additionalParentMatrix); + + assertEquals(newMatrix, newInstanceByBuilder.getMatrix()); + assertEquals(newMatrix, newInstanceByMethod.getMatrix()); + assertEquals(newInstanceByBuilder.hashCode(), newInstanceByMethod.hashCode()); } } diff --git a/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java b/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java index f1cfe24762a8..d54ce51c3c37 100644 --- a/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java @@ -26,16 +26,17 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import java.io.IOException; +import java.util.function.Supplier; @SmallTest @RunWith(AndroidJUnit4.class) public class ConfigParserTest { - private static final String SETTINGS = "int=42,float=12.3,boolean=true,string=abc"; + private static final Supplier<String> SETTINGS = + () -> "int=42,float=12.3,boolean=true,string=abc"; private static final String CLEAR_DEVICE_CONFIG_KEY_CMD = "device_config delete " + DeviceConfig.NAMESPACE_TEXTCLASSIFIER; private static final String[] DEVICE_CONFIG_KEYS = new String[]{ @@ -59,7 +60,6 @@ public class ConfigParserTest { } @Test - @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized public void getBoolean_deviceConfig() { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_TEXTCLASSIFIER, @@ -79,7 +79,6 @@ public class ConfigParserTest { } @Test - @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized public void getInt_deviceConfig() { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_TEXTCLASSIFIER, @@ -97,7 +96,6 @@ public class ConfigParserTest { } @Test - @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized public void getFloat_deviceConfig() { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_TEXTCLASSIFIER, @@ -115,7 +113,6 @@ public class ConfigParserTest { } @Test - @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized public void getString_deviceConfig() { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_TEXTCLASSIFIER, diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java index f6bb1bff3635..789b829456f3 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java @@ -50,9 +50,11 @@ public class TextClassificationConstantsTest { + "in_app_conversation_action_types_default=text_reply," + "notification_conversation_action_types_default=send_email:call_phone," + "lang_id_threshold_override=0.3," - + "lang_id_context_settings=10:1:0.5"; - final TextClassificationConstants constants = - TextClassificationConstants.loadFromString(s); + + "lang_id_context_settings=10:1:0.5," + + "detect_language_from_text_enabled=true," + + "template_intent_factory_enabled=true," + + "translate_in_classification_enabled=true"; + final TextClassificationConstants constants = new TextClassificationConstants(() -> s); assertWithMessage("local_textclassifier_enabled") .that(constants.isLocalTextClassifierEnabled()).isTrue(); @@ -95,6 +97,12 @@ public class TextClassificationConstantsTest { .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(0.3f); Assert.assertArrayEquals("lang_id_context_settings", constants.getLangIdContextSettings(), new float[]{10, 1, 0.5f}, EPSILON); + assertWithMessage("detect_language_from_text_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); + assertWithMessage("template_intent_factory_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); + assertWithMessage("translate_in_classification_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); } @Test @@ -116,9 +124,11 @@ public class TextClassificationConstantsTest { + "in_app_conversation_action_types_default=view_map:track_flight," + "notification_conversation_action_types_default=share_location," + "lang_id_threshold_override=2," - + "lang_id_context_settings=30:0.5:0.3"; - final TextClassificationConstants constants = - TextClassificationConstants.loadFromString(s); + + "lang_id_context_settings=30:0.5:0.3," + + "detect_language_from_text_enabled=false," + + "template_intent_factory_enabled=false," + + "translate_in_classification_enabled=false"; + final TextClassificationConstants constants = new TextClassificationConstants(() -> s); assertWithMessage("local_textclassifier_enabled") .that(constants.isLocalTextClassifierEnabled()).isFalse(); @@ -161,12 +171,17 @@ public class TextClassificationConstantsTest { .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(2f); Assert.assertArrayEquals("lang_id_context_settings", constants.getLangIdContextSettings(), new float[]{30, 0.5f, 0.3f}, EPSILON); + assertWithMessage("detect_language_from_text_enabled") + .that(constants.isLocalTextClassifierEnabled()).isFalse(); + assertWithMessage("template_intent_factory_enabled") + .that(constants.isLocalTextClassifierEnabled()).isFalse(); + assertWithMessage("translate_in_classification_enabled") + .that(constants.isLocalTextClassifierEnabled()).isFalse(); } @Test public void testLoadFromString_defaultValues() { - final TextClassificationConstants constants = - TextClassificationConstants.loadFromString(""); + final TextClassificationConstants constants = new TextClassificationConstants(() -> ""); assertWithMessage("local_textclassifier_enabled") .that(constants.isLocalTextClassifierEnabled()).isTrue(); @@ -213,5 +228,11 @@ public class TextClassificationConstantsTest { .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(-1f); Assert.assertArrayEquals("lang_id_context_settings", constants.getLangIdContextSettings(), new float[]{20, 1, 0.4f}, EPSILON); + assertWithMessage("detect_language_from_text_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); + assertWithMessage("template_intent_factory_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); + assertWithMessage("translate_in_classification_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); } } diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java index 4fcd51c9be1d..9148185d904b 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java @@ -77,7 +77,7 @@ public class TextClassificationManagerTest { TextClassifier fallback = TextClassifier.NO_OP; TextClassifier classifier = new TextClassifierImpl( - fakeContext, TextClassificationConstants.loadFromString(null), fallback); + fakeContext, new TextClassificationConstants(() -> null), fallback); String text = "Contact me at +12122537077"; String classifiedText = "+12122537077"; diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java index aeb8949c6976..e3eb2a313cf6 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java @@ -59,7 +59,7 @@ public class TextClassifierTest { // TODO: Implement TextClassifierService testing. private static final TextClassificationConstants TC_CONSTANTS = - TextClassificationConstants.loadFromString(""); + new TextClassificationConstants(() -> ""); private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US"); private static final String NO_TYPE = null; diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java index 3cb1e187fef1..b93c3a7a17e2 100644 --- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java +++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java @@ -57,7 +57,8 @@ public class RegisterStatusBarResultTest { 0x80 /* dockedStackSysUiVisibility */, new Binder() /* imeToken */, new Rect(0x100, 0x200, 0x400, 0x800) /* fullscreenStackBounds */, - new Rect(0x1000, 0x2000, 0x4000, 0x8000) /* dockedStackBounds */); + new Rect(0x1000, 0x2000, 0x4000, 0x8000) /* dockedStackBounds */, + true /* navbarColorManagedByIme */); final RegisterStatusBarResult copy = clone(original); @@ -80,6 +81,7 @@ public class RegisterStatusBarResultTest { assertThat(copy.mImeToken).isSameAs(original.mImeToken); assertThat(copy.mFullscreenStackBounds).isEqualTo(original.mFullscreenStackBounds); assertThat(copy.mDockedStackBounds).isEqualTo(original.mDockedStackBounds); + assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme); } private RegisterStatusBarResult clone(RegisterStatusBarResult original) { diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java new file mode 100644 index 000000000000..390bb766ab81 --- /dev/null +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019 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.overlaytest; + +import static java.util.concurrent.TimeUnit.SECONDS; + +import android.app.UiAutomation; +import android.content.res.Resources; +import android.os.ParcelFileDescriptor; + +import androidx.test.InstrumentationRegistry; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.concurrent.Executor; +import java.util.concurrent.FutureTask; + +class LocalOverlayManager { + private static final long TIMEOUT = 30; + + public static void setEnabledAndWait(Executor executor, final String packageName, + boolean enable) throws Exception { + final String pattern = (enable ? "[x]" : "[ ]") + " " + packageName; + if (executeShellCommand("cmd overlay list").contains(pattern)) { + // nothing to do, overlay already in the requested state + return; + } + + final Resources res = InstrumentationRegistry.getContext().getResources(); + final String[] oldApkPaths = res.getAssets().getApkPaths(); + FutureTask<Boolean> task = new FutureTask<>(() -> { + while (true) { + if (!Arrays.equals(oldApkPaths, res.getAssets().getApkPaths())) { + return true; + } + Thread.sleep(10); + } + }); + executor.execute(task); + executeShellCommand("cmd overlay " + (enable ? "enable " : "disable ") + packageName); + task.get(TIMEOUT, SECONDS); + } + + private static String executeShellCommand(final String command) + throws Exception { + final UiAutomation uiAutomation = + InstrumentationRegistry.getInstrumentation().getUiAutomation(); + final ParcelFileDescriptor pfd = uiAutomation.executeShellCommand(command); + try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) { + final BufferedReader reader = new BufferedReader( + new InputStreamReader(in, StandardCharsets.UTF_8)); + StringBuilder str = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + str.append(line); + } + return str.toString(); + } + } +} diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java index f86743a2fdbc..fdb6bbb4550b 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java @@ -21,13 +21,11 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.app.UiAutomation; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.os.LocaleList; -import android.os.ParcelFileDescriptor; import android.util.AttributeSet; import android.util.Xml; @@ -569,60 +567,4 @@ public abstract class OverlayBaseTest { setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 400, 600); } - - /** - * Executes the shell command and reads all the output to ensure the command ran and didn't - * get stuck buffering on output. - */ - protected static String executeShellCommand(UiAutomation automation, String command) - throws Exception { - final ParcelFileDescriptor pfd = automation.executeShellCommand(command); - try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) { - final BufferedReader reader = new BufferedReader( - new InputStreamReader(in, StandardCharsets.UTF_8)); - StringBuilder str = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - str.append(line); - } - return str.toString(); - } - } - - /** - * Enables overlay packages and waits for a configuration change event before - * returning, to guarantee that Resources are up-to-date. - * @param packages the list of package names to enable. - */ - protected static void enableOverlayPackages(String... packages) throws Exception { - enableOverlayPackages(true, packages); - } - - /** - * Disables overlay packages and waits for a configuration change event before - * returning, to guarantee that Resources are up-to-date. - * @param packages the list of package names to disable. - */ - protected static void disableOverlayPackages(String... packages) throws Exception { - enableOverlayPackages(false, packages); - } - - /** - * Enables/disables overlay packages and waits for a configuration change event before - * returning, to guarantee that Resources are up-to-date. - * @param enable enables the overlays when true, disables when false. - * @param packages the list of package names to enable/disable. - */ - private static void enableOverlayPackages(boolean enable, String[] packages) - throws Exception { - final UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation() - .getUiAutomation(); - for (final String pkg : packages) { - executeShellCommand(uiAutomation, - "cmd overlay " + (enable ? "enable " : "disable ") + pkg); - } - - // Wait for the overlay change to propagate. - Thread.sleep(1000); - } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java index cd3ed9dd5e86..d28c47d9c922 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java @@ -22,6 +22,8 @@ import org.junit.BeforeClass; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.util.concurrent.Executor; + @RunWith(JUnit4.class) @MediumTest public class WithMultipleOverlaysTest extends OverlayBaseTest { @@ -31,6 +33,9 @@ public class WithMultipleOverlaysTest extends OverlayBaseTest { @BeforeClass public static void enableOverlay() throws Exception { - enableOverlayPackages(APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG, FRAMEWORK_OVERLAY_PKG); + Executor executor = (cmd) -> new Thread(cmd).start(); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, true); + LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true); } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java index c0d4281b7e0d..6566ad304c1c 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java @@ -22,6 +22,8 @@ import org.junit.BeforeClass; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.util.concurrent.Executor; + @RunWith(JUnit4.class) @MediumTest public class WithOverlayTest extends OverlayBaseTest { @@ -31,7 +33,9 @@ public class WithOverlayTest extends OverlayBaseTest { @BeforeClass public static void enableOverlay() throws Exception { - disableOverlayPackages(APP_OVERLAY_TWO_PKG); - enableOverlayPackages(APP_OVERLAY_ONE_PKG, FRAMEWORK_OVERLAY_PKG); + Executor executor = (cmd) -> new Thread(cmd).start(); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false); + LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true); } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java index 33c7b2591fe2..48cfeab2fbff 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java @@ -22,6 +22,8 @@ import org.junit.BeforeClass; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.util.concurrent.Executor; + @RunWith(JUnit4.class) @MediumTest public class WithoutOverlayTest extends OverlayBaseTest { @@ -31,6 +33,9 @@ public class WithoutOverlayTest extends OverlayBaseTest { @BeforeClass public static void disableOverlays() throws Exception { - disableOverlayPackages(APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG, FRAMEWORK_OVERLAY_PKG); + Executor executor = (cmd) -> new Thread(cmd).start(); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, false); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false); + LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, false); } } diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp new file mode 100644 index 000000000000..37020fc8d79f --- /dev/null +++ b/data/etc/car/Android.bp @@ -0,0 +1,123 @@ +// Copyright (C} 2019 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. + + + +// Privapp permission whitelist files + +prebuilt_etc { + name: "privapp_whitelist_android.car.cluster.loggingrenderer", + sub_dir: "permissions", + src: "android.car.cluster.loggingrenderer.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_android.car.cluster.sample", + sub_dir: "permissions", + src: "android.car.cluster.sample.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_android.car.usb.handler", + sub_dir: "permissions", + src: "android.car.usb.handler.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.carlauncher", + sub_dir: "permissions", + src: "com.android.car.carlauncher.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.dialer", + sub_dir: "permissions", + src: "com.android.car.dialer.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.hvac", + sub_dir: "permissions", + src: "com.android.car.hvac.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.media", + sub_dir: "permissions", + src: "com.android.car.media.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.notification", + sub_dir: "permissions", + src: "com.android.car.notification.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.radio", + sub_dir: "permissions", + src: "com.android.car.radio.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.settings", + sub_dir: "permissions", + src: "com.android.car.settings.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.themeplayground", + sub_dir: "permissions", + src: "com.android.car.themeplayground.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.trust", + sub_dir: "permissions", + src: "com.android.car.trust.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car", + sub_dir: "permissions", + src: "com.android.car.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.google.android.car.kitchensink", + sub_dir: "permissions", + src: "com.google.android.car.kitchensink.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.developeroptions", + sub_dir: "permissions", + src: "com.android.car.developeroptions.xml", + filename_from_src: true, + product_specific: true, +} diff --git a/data/etc/car/android.car.cluster.loggingrenderer.xml b/data/etc/car/android.car.cluster.loggingrenderer.xml new file mode 100644 index 000000000000..784e0e7dde14 --- /dev/null +++ b/data/etc/car/android.car.cluster.loggingrenderer.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<permissions> + <privapp-permissions package="android.car.cluster.loggingrenderer"> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/android.car.cluster.sample.xml b/data/etc/car/android.car.cluster.sample.xml new file mode 100644 index 000000000000..75c57b8c1d07 --- /dev/null +++ b/data/etc/car/android.car.cluster.sample.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<permissions> + <privapp-permissions package="android.car.cluster.sample"> + <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/android.car.usb.handler.xml b/data/etc/car/android.car.usb.handler.xml new file mode 100644 index 000000000000..c67847c74cf8 --- /dev/null +++ b/data/etc/car/android.car.usb.handler.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<permissions> + <privapp-permissions package="android.car.usb.handler"> + <permission name="android.permission.MANAGE_USB"/> + <permission name="android.permission.MANAGE_USERS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.carlauncher.xml b/data/etc/car/com.android.car.carlauncher.xml new file mode 100644 index 000000000000..8ec1cd41dab4 --- /dev/null +++ b/data/etc/car/com.android.car.carlauncher.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<permissions> + <privapp-permissions package="com.android.car.carlauncher"> + <permission name="android.permission.ACTIVITY_EMBEDDING"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> + <permission name="android.permission.PACKAGE_USAGE_STATS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.developeroptions.xml b/data/etc/car/com.android.car.developeroptions.xml new file mode 100644 index 000000000000..76c8c622ba71 --- /dev/null +++ b/data/etc/car/com.android.car.developeroptions.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<permissions> + <privapp-permissions package="com.android.car.developeroptions"> + <permission name="android.permission.ACCESS_CHECKIN_PROPERTIES"/> + <permission name="android.permission.ACCESS_NOTIFICATIONS"/> + <permission name="android.permission.BACKUP"/> + <permission name="android.permission.BATTERY_STATS"/> + <permission name="android.permission.BLUETOOTH_PRIVILEGED"/> + <permission name="android.permission.CHANGE_APP_IDLE_STATE"/> + <permission name="android.permission.CHANGE_CONFIGURATION"/> + <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/> + <permission name="android.permission.DELETE_PACKAGES"/> + <permission name="android.permission.FORCE_STOP_PACKAGES"/> + <permission name="android.permission.LOCAL_MAC_ADDRESS"/> + <permission name="android.permission.MANAGE_DEBUGGING"/> + <permission name="android.permission.MANAGE_FINGERPRINT"/> + <permission name="android.permission.MANAGE_USB"/> + <permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MASTER_CLEAR"/> + <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> + <permission name="android.permission.MOVE_PACKAGE"/> + <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/> + <permission name="android.permission.PACKAGE_USAGE_STATS"/> + <permission name="android.permission.READ_SEARCH_INDEXABLES"/> + <permission name="android.permission.REBOOT"/> + <permission name="android.permission.SET_TIME"/> + <permission name="android.permission.STATUS_BAR"/> + <permission name="android.permission.TETHER_PRIVILEGED"/> + <permission name="android.permission.USE_RESERVED_DISK"/> + <permission name="android.permission.USER_ACTIVITY"/> + <permission name="android.permission.WRITE_APN_SETTINGS"/> + <permission name="android.permission.WRITE_MEDIA_STORAGE"/> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.dialer.xml b/data/etc/car/com.android.car.dialer.xml new file mode 100644 index 000000000000..d44f5a1704a2 --- /dev/null +++ b/data/etc/car/com.android.car.dialer.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<permissions> + <privapp-permissions package="com.android.car.dialer"> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.hvac.xml b/data/etc/car/com.android.car.hvac.xml new file mode 100644 index 000000000000..d3631e067f8b --- /dev/null +++ b/data/etc/car/com.android.car.hvac.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<permissions> + <privapp-permissions package="com.android.car.hvac"> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.media.xml b/data/etc/car/com.android.car.media.xml new file mode 100644 index 000000000000..d17453d26e59 --- /dev/null +++ b/data/etc/car/com.android.car.media.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<permissions> + <privapp-permissions package="com.android.car.media"> + <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.notification.xml b/data/etc/car/com.android.car.notification.xml new file mode 100644 index 000000000000..8479512b049e --- /dev/null +++ b/data/etc/car/com.android.car.notification.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<permissions> + <privapp-permissions package="com.android.car.notification"> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.radio.xml b/data/etc/car/com.android.car.radio.xml new file mode 100644 index 000000000000..d7853aba2d90 --- /dev/null +++ b/data/etc/car/com.android.car.radio.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<permissions> + <privapp-permissions package="com.android.car.radio"> + <permission name="android.permission.ACCESS_BROADCAST_RADIO"/> + <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.settings.xml b/data/etc/car/com.android.car.settings.xml new file mode 100644 index 000000000000..5f7e1c13decf --- /dev/null +++ b/data/etc/car/com.android.car.settings.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<permissions> + <privapp-permissions package="com.android.car.settings"> + <permission name="android.permission.ACCESS_CHECKIN_PROPERTIES"/> + <permission name="android.permission.ACCESS_NOTIFICATIONS"/> + <permission name="android.permission.BACKUP"/> + <permission name="android.permission.BATTERY_STATS"/> + <permission name="android.permission.BLUETOOTH_PRIVILEGED"/> + <permission name="android.permission.CHANGE_APP_IDLE_STATE"/> + <permission name="android.permission.CHANGE_CONFIGURATION"/> + <permission name="android.permission.DELETE_PACKAGES"/> + <permission name="android.permission.DELETE_CACHE_FILES"/> + <permission name="android.permission.DUMP"/> + <permission name="android.permission.FORCE_STOP_PACKAGES"/> + <permission name="android.permission.LOCAL_MAC_ADDRESS"/> + <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/> + <permission name="android.permission.MANAGE_DEBUGGING"/> + <permission name="android.permission.MANAGE_DEVICE_ADMINS"/> + <permission name="android.permission.MANAGE_FINGERPRINT"/> + <permission name="android.permission.MANAGE_USB"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE" /> + <permission name="android.permission.MASTER_CLEAR"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> + <permission name="android.permission.MOVE_PACKAGE"/> + <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/> + <permission name="android.permission.PACKAGE_USAGE_STATS"/> + <permission name="android.permission.READ_SEARCH_INDEXABLES"/> + <permission name="android.permission.REBOOT"/> + <permission name="android.permission.SET_TIME"/> + <permission name="android.permission.SET_TIME_ZONE"/> + <permission name="android.permission.STATUS_BAR"/> + <permission name="android.permission.TETHER_PRIVILEGED"/> + <permission name="android.permission.USE_RESERVED_DISK"/> + <permission name="android.permission.USER_ACTIVITY"/> + <permission name="android.permission.WRITE_APN_SETTINGS"/> + <permission name="android.permission.WRITE_MEDIA_STORAGE"/> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" /> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.themeplayground.xml b/data/etc/car/com.android.car.themeplayground.xml new file mode 100644 index 000000000000..cab4718b0461 --- /dev/null +++ b/data/etc/car/com.android.car.themeplayground.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<permissions> + <privapp-permissions package="com.android.car.themeplayground"> + <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.trust.xml b/data/etc/car/com.android.car.trust.xml new file mode 100644 index 000000000000..dc87af278b27 --- /dev/null +++ b/data/etc/car/com.android.car.trust.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<permissions> + <privapp-permissions package="com.android.car.trust"> + <permission name="android.permission.BLUETOOTH" /> + <permission name="android.permission.BLUETOOTH_ADMIN" /> + <permission name="android.permission.ACCESS_COARSE_LOCATION"/> + <permission name="android.permission.ACCESS_FINE_LOCATION"/> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + <permission name="android.permission.INTERACT_ACROSS_USERS_FULL" /> + <permission name="android.permission.MANAGE_USERS" /> + <permission name="android.permission.CONTROL_KEYGUARD" /> + <permission name="android.permission.PROVIDE_TRUST_AGENT" /> + <permission name="android.permission.RECEIVE_BOOT_COMPLETED" /> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.xml b/data/etc/car/com.android.car.xml new file mode 100644 index 000000000000..f1797deed075 --- /dev/null +++ b/data/etc/car/com.android.car.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<permissions> + <privapp-permissions package="com.android.car"> + <permission name="android.permission.LOCATION_HARDWARE"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MODIFY_AUDIO_ROUTING"/> + <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.PROVIDE_TRUST_AGENT"/> + <permission name="android.permission.REAL_GET_TASKS"/> + <permission name="android.permission.REBOOT"/> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml new file mode 100644 index 000000000000..6b26e8f3d5f2 --- /dev/null +++ b/data/etc/car/com.google.android.car.kitchensink.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<permissions> + <privapp-permissions package="com.google.android.car.kitchensink"> + <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/> + <permission name="android.permission.LOCATION_HARDWARE"/> + <permission name="android.permission.MANAGE_USB"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MODIFY_AUDIO_ROUTING"/> + <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.PROVIDE_TRUST_AGENT"/> + <permission name="android.permission.REAL_GET_TASKS"/> + <permission name="android.permission.REBOOT"/> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 485add9fa11f..27e859c3987c 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -83,6 +83,7 @@ applications that come with the platform <permission name="android.permission.SET_TIME_ZONE"/> <permission name="android.permission.SHUTDOWN"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/> </privapp-permissions> <privapp-permissions package="com.android.mms.service"> @@ -235,6 +236,7 @@ applications that come with the platform <permission name="android.permission.CALL_PRIVILEGED"/> <permission name="android.permission.INTERACT_ACROSS_USERS"/> <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MANAGE_ROLE_HOLDERS"/> <permission name="android.permission.MODIFY_AUDIO_ROUTING" /> <permission name="android.permission.MODIFY_PHONE_STATE"/> <permission name="android.permission.STOP_APP_SWITCHES"/> diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 07f81c18e1ca..44710178da5e 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -2197,8 +2197,12 @@ public final class Bitmap implements Parcelable { } /** - * * @return {@link GraphicBuffer} which is internally used by hardware bitmap + * + * Note: the GraphicBuffer does *not* have an associated {@link ColorSpace}. + * To render this object the same as its rendered with this Bitmap, you + * should also call {@link getColorSpace}. + * * @hide */ @UnsupportedAppUsage diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index 816d1fda57d1..11d635eaba12 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -637,6 +637,7 @@ public class GradientDrawable extends Drawable { * @see #setOrientation(Orientation) */ public Orientation getOrientation() { + updateGradientStateOrientation(); return mGradientState.mOrientation; } @@ -653,6 +654,9 @@ public class GradientDrawable extends Drawable { * @see #getOrientation() */ public void setOrientation(Orientation orientation) { + // Update the angle here so that subsequent attempts to obtain the orientation + // from the angle overwrite previously configured values during inflation + mGradientState.mAngle = getAngleFromOrientation(orientation); mGradientState.mOrientation = orientation; mGradientIsDirty = true; invalidateSelf(); @@ -1242,6 +1246,76 @@ public class GradientDrawable extends Drawable { } /** + * Update the orientation of the gradient based on the given angle only if the type is + * {@link #LINEAR_GRADIENT} + */ + private void updateGradientStateOrientation() { + if (mGradientState.mGradient == LINEAR_GRADIENT) { + int angle = mGradientState.mAngle; + if (angle % 45 != 0) { + throw new IllegalArgumentException("Linear gradient requires 'angle' attribute to " + + "be a multiple of 45"); + } + + Orientation orientation; + switch (angle) { + case 0: + orientation = Orientation.LEFT_RIGHT; + break; + case 45: + orientation = Orientation.BL_TR; + break; + case 90: + orientation = Orientation.BOTTOM_TOP; + break; + case 135: + orientation = Orientation.BR_TL; + break; + case 180: + orientation = Orientation.RIGHT_LEFT; + break; + case 225: + orientation = Orientation.TR_BL; + break; + case 270: + orientation = Orientation.TOP_BOTTOM; + break; + case 315: + orientation = Orientation.TL_BR; + break; + default: + // Should not get here as exception is thrown above if angle is not multiple + // of 45 degrees + orientation = Orientation.LEFT_RIGHT; + break; + } + mGradientState.mOrientation = orientation; + } + } + + private int getAngleFromOrientation(Orientation orientation) { + switch (orientation) { + default: + case LEFT_RIGHT: + return 0; + case BL_TR: + return 45; + case BOTTOM_TOP: + return 90; + case BR_TL: + return 135; + case RIGHT_LEFT: + return 180; + case TR_BL: + return 225; + case TOP_BOTTOM: + return 270; + case TL_BR: + return 315; + } + } + + /** * This checks mGradientIsDirty, and if it is true, recomputes both our drawing * rectangle (mRect) and the gradient itself, since it depends on our * rectangle too. @@ -1270,6 +1344,7 @@ public class GradientDrawable extends Drawable { if (st.mGradient == LINEAR_GRADIENT) { final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f; + updateGradientStateOrientation(); switch (st.mOrientation) { case TOP_BOTTOM: x0 = r.left; y0 = r.top; @@ -1312,10 +1387,6 @@ public class GradientDrawable extends Drawable { y0 = r.top + (r.bottom - r.top) * st.mCenterY; float radius = st.mGradientRadius; - if (Float.compare(radius, 0.0f) == -1 || Float.isNaN(radius)) { - throw new IllegalArgumentException("Gradient radius must be a valid " - + "number greater than or equal to 0"); - } if (st.mGradientRadiusType == RADIUS_TYPE_FRACTION) { // Fall back to parent width or height if intrinsic // size is not specified. @@ -1511,8 +1582,6 @@ public class GradientDrawable extends Drawable { st.mAttrGradient, R.styleable.GradientDrawableGradient); try { updateGradientDrawableGradient(t.getResources(), a); - } catch (XmlPullParserException e) { - rethrowAsRuntimeException(e); } finally { a.recycle(); } @@ -1700,8 +1769,7 @@ public class GradientDrawable extends Drawable { } } - private void updateGradientDrawableGradient(Resources r, TypedArray a) - throws XmlPullParserException { + private void updateGradientDrawableGradient(Resources r, TypedArray a) { final GradientState st = mGradientState; // Account for any configuration changes. @@ -1764,42 +1832,7 @@ public class GradientDrawable extends Drawable { } int angle = (int) a.getFloat(R.styleable.GradientDrawableGradient_angle, st.mAngle); - angle %= 360; - - if (angle % 45 != 0) { - throw new XmlPullParserException(a.getPositionDescription() - + "<gradient> tag requires 'angle' attribute to " - + "be a multiple of 45"); - } - - st.mAngle = angle; - - switch (angle) { - case 0: - st.mOrientation = Orientation.LEFT_RIGHT; - break; - case 45: - st.mOrientation = Orientation.BL_TR; - break; - case 90: - st.mOrientation = Orientation.BOTTOM_TOP; - break; - case 135: - st.mOrientation = Orientation.BR_TL; - break; - case 180: - st.mOrientation = Orientation.RIGHT_LEFT; - break; - case 225: - st.mOrientation = Orientation.TR_BL; - break; - case 270: - st.mOrientation = Orientation.TOP_BOTTOM; - break; - case 315: - st.mOrientation = Orientation.TL_BR; - break; - } + st.mAngle = angle % 360; final TypedValue tv = a.peekValue(R.styleable.GradientDrawableGradient_gradientRadius); if (tv != null) { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 9248eadbd0ef..8092b1d10659 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -166,8 +166,6 @@ static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) { auto min_undequeued_buffers = static_cast<uint32_t>(query_value); int bufferCount = min_undequeued_buffers + 2 + extraBuffers; - ALOGD("Setting buffer count to %d, min_undequeued %u, extraBuffers %u", - bufferCount, min_undequeued_buffers, extraBuffers); native_window_set_buffer_count(window, bufferCount); } diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 1d553342415c..159cf497384a 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -235,8 +235,6 @@ bool EglManager::hasEglContext() { } void EglManager::loadConfigs() { - ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior)); - // Note: The default pixel format is RGBA_8888, when other formats are // available, we should check the target pixel format and configure the // attributes list properly. @@ -246,7 +244,6 @@ void EglManager::loadConfigs() { // Try again without dirty regions enabled ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); mSwapBehavior = SwapBehavior::Discard; - ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior)); mEglConfig = load8BitsConfig(mEglDisplay, mSwapBehavior); } else { // Failed to get a valid config diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index 977e790eb42e..8612e1bf324c 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -816,6 +816,8 @@ public final class AudioAttributes implements Parcelable { */ @UnsupportedAppUsage public Builder setInternalLegacyStreamType(int streamType) { + mContentType = CONTENT_TYPE_UNKNOWN; + mUsage = USAGE_UNKNOWN; if (AudioProductStrategy.getAudioProductStrategies().size() > 0) { AudioAttributes attributes = AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType( @@ -828,49 +830,52 @@ public final class AudioAttributes implements Parcelable { mTags = attributes.mTags; mBundle = attributes.mBundle; mSource = attributes.mSource; - return this; } } - switch(streamType) { - case AudioSystem.STREAM_VOICE_CALL: - mContentType = CONTENT_TYPE_SPEECH; - break; - case AudioSystem.STREAM_SYSTEM_ENFORCED: - mFlags |= FLAG_AUDIBILITY_ENFORCED; - // intended fall through, attributes in common with STREAM_SYSTEM - case AudioSystem.STREAM_SYSTEM: - mContentType = CONTENT_TYPE_SONIFICATION; - break; - case AudioSystem.STREAM_RING: - mContentType = CONTENT_TYPE_SONIFICATION; - break; - case AudioSystem.STREAM_MUSIC: - mContentType = CONTENT_TYPE_MUSIC; - break; - case AudioSystem.STREAM_ALARM: - mContentType = CONTENT_TYPE_SONIFICATION; - break; - case AudioSystem.STREAM_NOTIFICATION: - mContentType = CONTENT_TYPE_SONIFICATION; - break; - case AudioSystem.STREAM_BLUETOOTH_SCO: - mContentType = CONTENT_TYPE_SPEECH; - mFlags |= FLAG_SCO; - break; - case AudioSystem.STREAM_DTMF: - mContentType = CONTENT_TYPE_SONIFICATION; - break; - case AudioSystem.STREAM_TTS: - mContentType = CONTENT_TYPE_SONIFICATION; - mFlags |= FLAG_BEACON; - break; - case AudioSystem.STREAM_ACCESSIBILITY: - mContentType = CONTENT_TYPE_SPEECH; - break; - default: - Log.e(TAG, "Invalid stream type " + streamType + " for AudioAttributes"); + if (mContentType == CONTENT_TYPE_UNKNOWN) { + switch (streamType) { + case AudioSystem.STREAM_VOICE_CALL: + mContentType = CONTENT_TYPE_SPEECH; + break; + case AudioSystem.STREAM_SYSTEM_ENFORCED: + mFlags |= FLAG_AUDIBILITY_ENFORCED; + // intended fall through, attributes in common with STREAM_SYSTEM + case AudioSystem.STREAM_SYSTEM: + mContentType = CONTENT_TYPE_SONIFICATION; + break; + case AudioSystem.STREAM_RING: + mContentType = CONTENT_TYPE_SONIFICATION; + break; + case AudioSystem.STREAM_MUSIC: + mContentType = CONTENT_TYPE_MUSIC; + break; + case AudioSystem.STREAM_ALARM: + mContentType = CONTENT_TYPE_SONIFICATION; + break; + case AudioSystem.STREAM_NOTIFICATION: + mContentType = CONTENT_TYPE_SONIFICATION; + break; + case AudioSystem.STREAM_BLUETOOTH_SCO: + mContentType = CONTENT_TYPE_SPEECH; + mFlags |= FLAG_SCO; + break; + case AudioSystem.STREAM_DTMF: + mContentType = CONTENT_TYPE_SONIFICATION; + break; + case AudioSystem.STREAM_TTS: + mContentType = CONTENT_TYPE_SONIFICATION; + mFlags |= FLAG_BEACON; + break; + case AudioSystem.STREAM_ACCESSIBILITY: + mContentType = CONTENT_TYPE_SPEECH; + break; + default: + Log.e(TAG, "Invalid stream type " + streamType + " for AudioAttributes"); + } + } + if (mUsage == USAGE_UNKNOWN) { + mUsage = usageForStreamType(streamType); } - mUsage = usageForStreamType(streamType); return this; } diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index a56e7f583094..f304f7cc5981 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -24,6 +24,7 @@ import android.annotation.Nullable; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.os.Build; +import android.os.SystemProperties; import android.util.Log; import android.util.Pair; import android.util.Range; @@ -1105,7 +1106,11 @@ public final class MediaCodecInfo { mBitrateRange = Range.create(0, Integer.MAX_VALUE); mMaxInputChannelCount = MAX_INPUT_CHANNEL_COUNT; // mBitrateRange = Range.create(1, 320000); - mSampleRateRanges = new Range[] { Range.create(8000, 96000) }; + final int minSampleRate = SystemProperties. + getInt("ro.mediacodec.min_sample_rate", 7350); + final int maxSampleRate = SystemProperties. + getInt("ro.mediacodec.max_sample_rate", 192000); + mSampleRateRanges = new Range[] { Range.create(minSampleRate, maxSampleRate) }; mSampleRates = null; } @@ -1677,6 +1682,13 @@ public final class MediaCodecInfo { return "PerformancePoint(" + info + ")"; } + @Override + public int hashCode() { + // only max frame rate must equal between performance points that equal to one + // another + return mMaxFrameRate; + } + /** * Create a detailed performance point with custom max frame rate and macroblock size. * diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java index 21b194d73ff9..5de56c718570 100644 --- a/media/java/android/media/ThumbnailUtils.java +++ b/media/java/android/media/ThumbnailUtils.java @@ -244,30 +244,77 @@ public class ThumbnailUtils { final Resizer resizer = new Resizer(size, signal); final String mimeType = MediaFile.getMimeTypeForFile(file.getName()); + Bitmap bitmap = null; + ExifInterface exif = null; + int orientation = 0; + + // get orientation + if (MediaFile.isExifMimeType(mimeType)) { + exif = new ExifInterface(file); + switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0)) { + case ExifInterface.ORIENTATION_ROTATE_90: + orientation = 90; + break; + case ExifInterface.ORIENTATION_ROTATE_180: + orientation = 180; + break; + case ExifInterface.ORIENTATION_ROTATE_270: + orientation = 270; + break; + } + } + + boolean isHeifFile = false; + if (mimeType.equals("image/heif") || mimeType.equals("image/heif-sequence") || mimeType.equals("image/heic") || mimeType.equals("image/heic-sequence")) { + isHeifFile = true; try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) { retriever.setDataSource(file.getAbsolutePath()); - return retriever.getThumbnailImageAtIndex(-1, + bitmap = retriever.getThumbnailImageAtIndex(-1, new MediaMetadataRetriever.BitmapParams(), size.getWidth(), size.getWidth() * size.getHeight()); } catch (RuntimeException e) { throw new IOException("Failed to create thumbnail", e); } - } else if (MediaFile.isExifMimeType(mimeType)) { - final ExifInterface exif = new ExifInterface(file); + } + + if (bitmap == null && exif != null) { final byte[] raw = exif.getThumbnailBytes(); if (raw != null) { - return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer); + try { + bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer); + } catch (ImageDecoder.DecodeException e) { + Log.w(TAG, e); + } } } // Checkpoint before going deeper if (signal != null) signal.throwIfCanceled(); - return ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), resizer); + if (bitmap == null) { + bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), resizer); + // Use ImageDecoder to do full image decode of heif format file + // will have right orientation. Don't rotate the bitmap again. + if (isHeifFile) { + return bitmap; + } + } + + // Transform the bitmap if the orientation of the image is not 0. + if (orientation != 0 && bitmap != null) { + final int width = bitmap.getWidth(); + final int height = bitmap.getHeight(); + + final Matrix m = new Matrix(); + m.setRotate(orientation, width / 2, height / 2); + bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false); + } + + return bitmap; } /** diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 12b41cabd142..07dbf93ce46e 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -951,12 +951,10 @@ public class CarStatusBar extends StatusBar implements return mContext.getDrawable(com.android.internal.R.drawable.default_wallpaper); } - /** Returns true if the current user makes it through the setup wizard, false otherwise. */ - private boolean getIsUserSetup() { - return mUserSetup; - } - private void setNotificationViewClipBounds(int height) { + if (height > mNotificationView.getHeight()) { + height = mNotificationView.getHeight(); + } Rect clipBounds = new Rect(); clipBounds.set(0, 0, mNotificationView.getWidth(), height); // Sets the clip region on the notification list view. @@ -980,7 +978,6 @@ public class CarStatusBar extends StatusBar implements } } - private static final int SWIPE_UP_MIN_DISTANCE = 75; private static final int SWIPE_DOWN_MIN_DISTANCE = 25; private static final int SWIPE_MAX_OFF_PATH = 75; private static final int SWIPE_THRESHOLD_VELOCITY = 200; diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java index b2baff5db75b..73c789516754 100644 --- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java +++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java @@ -23,6 +23,7 @@ import static android.service.notification.NotificationListenerService.Ranking.U import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.app.ActivityThread; import android.app.INotificationManager; import android.app.Notification; @@ -71,6 +72,7 @@ import java.util.concurrent.Executors; /** * Notification assistant that provides guidance on notification channel blocking */ +@SuppressLint("OverrideAbstract") public class Assistant extends NotificationAssistantService { private static final String TAG = "ExtAssistant"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -238,7 +240,7 @@ public class Assistant extends NotificationAssistantService { } mSingleThreadExecutor.submit(() -> { NotificationEntry entry = - new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + new NotificationEntry(getContext(), mPackageManager, sbn, channel, mSmsHelper); SmartActionsHelper.SmartSuggestions suggestions = mSmartActionsHelper.suggest(entry); if (DEBUG) { Log.d(TAG, String.format( @@ -295,7 +297,7 @@ public class Assistant extends NotificationAssistantService { } Ranking ranking = getRanking(sbn.getKey(), rankingMap); if (ranking != null && ranking.getChannel() != null) { - NotificationEntry entry = new NotificationEntry(mPackageManager, + NotificationEntry entry = new NotificationEntry(getContext(), mPackageManager, sbn, ranking.getChannel(), mSmsHelper); String key = getKey( sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId()); diff --git a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java index 84a8a8c577f7..1ffbac941dc3 100644 --- a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java +++ b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java @@ -28,18 +28,23 @@ import android.app.NotificationChannel; import android.app.Person; import android.app.RemoteInput; import android.content.ComponentName; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.graphics.drawable.Icon; import android.media.AudioAttributes; import android.media.AudioSystem; import android.os.Build; +import android.os.Parcelable; import android.os.RemoteException; import android.service.notification.StatusBarNotification; import android.util.Log; +import android.util.SparseArray; import java.util.ArrayList; import java.util.Objects; +import java.util.Set; /** * Holds data about notifications. @@ -47,6 +52,10 @@ import java.util.Objects; public class NotificationEntry { static final String TAG = "NotificationEntry"; + // Copied from hidden definitions in Notification.TvExtender + private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS"; + + private final Context mContext; private final StatusBarNotification mSbn; private final IPackageManager mPackageManager; private int mTargetSdkVersion = Build.VERSION_CODES.N_MR1; @@ -60,9 +69,10 @@ public class NotificationEntry { private final Object mLock = new Object(); - public NotificationEntry(IPackageManager packageManager, StatusBarNotification sbn, - NotificationChannel channel, SmsHelper smsHelper) { - mSbn = sbn; + public NotificationEntry(Context applicationContext, IPackageManager packageManager, + StatusBarNotification sbn, NotificationChannel channel, SmsHelper smsHelper) { + mContext = applicationContext; + mSbn = cloneStatusBarNotificationLight(sbn); mChannel = channel; mPackageManager = packageManager; mPreChannelsNotification = isPreChannelsNotification(); @@ -71,6 +81,66 @@ public class NotificationEntry { mSmsHelper = smsHelper; } + /** Adapted from {@code Notification.lightenPayload}. */ + @SuppressWarnings("nullness") + private static void lightenNotificationPayload(Notification notification) { + notification.tickerView = null; + notification.contentView = null; + notification.bigContentView = null; + notification.headsUpContentView = null; + notification.largeIcon = null; + if (notification.extras != null && !notification.extras.isEmpty()) { + final Set<String> keyset = notification.extras.keySet(); + final int keysetSize = keyset.size(); + final String[] keys = keyset.toArray(new String[keysetSize]); + for (int i = 0; i < keysetSize; i++) { + final String key = keys[i]; + if (EXTRA_TV_EXTENDER.equals(key) + || Notification.EXTRA_MESSAGES.equals(key) + || Notification.EXTRA_MESSAGING_PERSON.equals(key) + || Notification.EXTRA_PEOPLE_LIST.equals(key)) { + continue; + } + final Object obj = notification.extras.get(key); + if (obj != null + && (obj instanceof Parcelable + || obj instanceof Parcelable[] + || obj instanceof SparseArray + || obj instanceof ArrayList)) { + notification.extras.remove(key); + } + } + } + } + + /** An interpretation of {@code Notification.cloneInto} with heavy=false. */ + private Notification cloneNotificationLight(Notification notification) { + // We can't just use clone() here because the only way to remove the icons is with the + // builder, which we can only create with a Context. + Notification lightNotification = + Notification.Builder.recoverBuilder(mContext, notification) + .setSmallIcon(0) + .setLargeIcon((Icon) null) + .build(); + lightenNotificationPayload(lightNotification); + return lightNotification; + } + + /** Adapted from {@code StatusBarNotification.cloneLight}. */ + public StatusBarNotification cloneStatusBarNotificationLight(StatusBarNotification sbn) { + return new StatusBarNotification( + sbn.getPackageName(), + sbn.getOpPkg(), + sbn.getId(), + sbn.getTag(), + sbn.getUid(), + /*initialPid=*/ 0, + /*score=*/ 0, + cloneNotificationLight(sbn.getNotification()), + sbn.getUser(), + sbn.getPostTime()); + } + private boolean isPreChannelsNotification() { try { ApplicationInfo info = mPackageManager.getApplicationInfo( diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java index 3db275acfd06..a87d57cf872b 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java @@ -102,7 +102,8 @@ public class AgingHelperTest { public void testNoSnoozingOnPost() { NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); StatusBarNotification sbn = generateSbn(channel.getId()); - NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, sbn, channel, mSmsHelper); mAgingHelper.onNotificationPosted(entry); @@ -113,7 +114,8 @@ public class AgingHelperTest { public void testPostResetsSnooze() { NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); StatusBarNotification sbn = generateSbn(channel.getId()); - NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, sbn, channel, mSmsHelper); mAgingHelper.onNotificationPosted(entry); @@ -124,7 +126,8 @@ public class AgingHelperTest { public void testSnoozingOnSeen() { NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); StatusBarNotification sbn = generateSbn(channel.getId()); - NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, sbn, channel, mSmsHelper); entry.setSeen(); when(mCategorizer.getCategory(entry)).thenReturn(NotificationCategorizer.CATEGORY_PEOPLE); @@ -137,7 +140,8 @@ public class AgingHelperTest { NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); StatusBarNotification sbn = generateSbn(channel.getId()); - NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, sbn, channel, mSmsHelper); when(mCategorizer.getCategory(entry)).thenReturn(NotificationCategorizer.CATEGORY_PEOPLE); mAgingHelper.onNotificationSeen(entry); diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java index ee29bc594801..012dcc01fe6f 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java @@ -468,8 +468,10 @@ public class AssistantTest extends ServiceTestCase<Assistant> { @Test public void testAssistantNeverIncreasesImportanceWhenSuggestingSilent() throws Exception { StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C3, "min notif!", null); - Adjustment adjust = mAssistant.createEnqueuedNotificationAdjustment(new NotificationEntry( - mPackageManager, sbn, P1C3, mSmsHelper), new ArrayList<>(), new ArrayList<>()); + Adjustment adjust = mAssistant.createEnqueuedNotificationAdjustment( + new NotificationEntry(mContext, mPackageManager, sbn, P1C3, mSmsHelper), + new ArrayList<>(), + new ArrayList<>()); assertEquals(IMPORTANCE_MIN, adjust.getSignals().getInt(Adjustment.KEY_IMPORTANCE)); } } diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java index f51e911cbe97..c02607927b1b 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java @@ -24,6 +24,7 @@ import static android.media.AudioAttributes.USAGE_ALARM; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; @@ -34,6 +35,8 @@ import android.app.Person; import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; +import android.graphics.Bitmap; +import android.graphics.drawable.Icon; import android.media.AudioAttributes; import android.os.Build; import android.os.Process; @@ -41,9 +44,6 @@ import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.testing.TestableContext; -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -53,6 +53,9 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + @RunWith(AndroidJUnit4.class) public class NotificationEntryTest { private String mPkg = "pkg"; @@ -113,7 +116,8 @@ public class NotificationEntryTest { people.add(new Person.Builder().setKey("mailto:testing@android.com").build()); sbn.getNotification().extras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, people); - NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, sbn, channel, mSmsHelper); assertTrue(entry.involvesPeople()); } @@ -121,7 +125,8 @@ public class NotificationEntryTest { public void testNotPerson() { NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); StatusBarNotification sbn = generateSbn(channel.getId()); - NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, sbn, channel, mSmsHelper); assertFalse(entry.involvesPeople()); } @@ -129,7 +134,8 @@ public class NotificationEntryTest { public void testHasPerson_matchesDefaultSmsApp() { NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); StatusBarNotification sbn = generateSbn(channel.getId(), DEFAULT_SMS_PACKAGE_NAME); - NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, sbn, channel, mSmsHelper); assertTrue(entry.involvesPeople()); } @@ -137,7 +143,8 @@ public class NotificationEntryTest { public void testHasPerson_doesntMatchDefaultSmsApp() { NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); StatusBarNotification sbn = generateSbn(channel.getId(), "abc"); - NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, sbn, channel, mSmsHelper); assertFalse(entry.involvesPeople()); } @@ -148,8 +155,8 @@ public class NotificationEntryTest { Notification n = new Notification.Builder(mContext, channel.getId()) .setStyle(new Notification.InboxStyle()) .build(); - NotificationEntry entry = - new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, generateSbn(n), channel, mSmsHelper); assertTrue(entry.hasStyle(Notification.InboxStyle.class)); } @@ -160,8 +167,8 @@ public class NotificationEntryTest { Notification n = new Notification.Builder(mContext, channel.getId()) .setStyle(new Notification.MessagingStyle("")) .build(); - NotificationEntry entry = - new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, generateSbn(n), channel, mSmsHelper); assertTrue(entry.hasStyle(Notification.MessagingStyle.class)); } @@ -172,8 +179,8 @@ public class NotificationEntryTest { Notification n = new Notification.Builder(mContext, channel.getId()) .setStyle(new Notification.BigPictureStyle()) .build(); - NotificationEntry entry = - new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, generateSbn(n), channel, mSmsHelper); assertFalse(entry.hasStyle(Notification.InboxStyle.class)); assertFalse(entry.hasStyle(Notification.MessagingStyle.class)); } @@ -184,7 +191,7 @@ public class NotificationEntryTest { channel.setSound(null, new AudioAttributes.Builder().setUsage(USAGE_ALARM).build()); NotificationEntry entry = new NotificationEntry( - mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper); + mContext, mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper); assertTrue(entry.isAudioAttributesUsage(USAGE_ALARM)); } @@ -193,7 +200,7 @@ public class NotificationEntryTest { public void testIsNotAudioAttributes() { NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); NotificationEntry entry = new NotificationEntry( - mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper); + mContext, mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper); assertFalse(entry.isAudioAttributesUsage(USAGE_ALARM)); } @@ -205,8 +212,8 @@ public class NotificationEntryTest { Notification n = new Notification.Builder(mContext, channel.getId()) .setCategory(Notification.CATEGORY_EMAIL) .build(); - NotificationEntry entry = - new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, generateSbn(n), channel, mSmsHelper); assertTrue(entry.isCategory(Notification.CATEGORY_EMAIL)); assertFalse(entry.isCategory(Notification.CATEGORY_MESSAGE)); @@ -219,8 +226,8 @@ public class NotificationEntryTest { Notification n = new Notification.Builder(mContext, channel.getId()) .setFlag(FLAG_FOREGROUND_SERVICE, true) .build(); - NotificationEntry entry = - new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, generateSbn(n), channel, mSmsHelper); assertTrue(entry.isOngoing()); } @@ -232,9 +239,28 @@ public class NotificationEntryTest { Notification n = new Notification.Builder(mContext, channel.getId()) .setFlag(FLAG_CAN_COLORIZE, true) .build(); - NotificationEntry entry = - new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, generateSbn(n), channel, mSmsHelper); assertFalse(entry.isOngoing()); } + + @Test + public void testShrinkNotification() { + Notification n = new Notification.Builder(mContext, "") + .setLargeIcon(Icon.createWithResource( + mContext, android.R.drawable.alert_dark_frame)) + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .build(); + n.largeIcon = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); + NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); + + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, generateSbn(n), channel, mSmsHelper); + + assertNull(entry.getNotification().getSmallIcon()); + assertNull(entry.getNotification().getLargeIcon()); + assertNull(entry.getNotification().largeIcon); + assertNull(entry.getNotification().extras.getParcelable(Notification.EXTRA_LARGE_ICON)); + } } diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java index dfa1ea0aac90..69abe87cd429 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java @@ -46,9 +46,6 @@ import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassifier; import android.view.textclassifier.TextClassifierEvent; -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - import com.google.common.truth.FailureStrategy; import com.google.common.truth.Subject; import com.google.common.truth.SubjectFactory; @@ -71,9 +68,11 @@ import java.util.Objects; import javax.annotation.Nullable; +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + @RunWith(AndroidJUnit4.class) public class SmartActionsHelperTest { - private static final String NOTIFICATION_KEY = "key"; private static final String RESULT_ID = "id"; private static final float SCORE = 0.7f; private static final CharSequence SMART_REPLY = "Home"; @@ -88,7 +87,6 @@ public class SmartActionsHelperTest { IPackageManager mIPackageManager; @Mock private TextClassifier mTextClassifier; - @Mock private StatusBarNotification mStatusBarNotification; @Mock private SmsHelper mSmsHelper; @@ -108,9 +106,6 @@ public class SmartActionsHelperTest { when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class))) .thenReturn(new ConversationActions(Arrays.asList(REPLY_ACTION), RESULT_ID)); - when(mStatusBarNotification.getPackageName()).thenReturn("random.app"); - when(mStatusBarNotification.getUser()).thenReturn(Process.myUserHandle()); - when(mStatusBarNotification.getKey()).thenReturn(NOTIFICATION_KEY); mNotificationBuilder = new Notification.Builder(mContext, "channel"); mSettings = AssistantSettings.createForTesting( null, null, Process.myUserHandle().getIdentifier(), null); @@ -119,10 +114,15 @@ public class SmartActionsHelperTest { mSmartActionsHelper = new SmartActionsHelper(mContext, mSettings); } + private void setStatusBarNotification(Notification n) { + mStatusBarNotification = new StatusBarNotification("random.app", "random.app", 0, + "tag", Process.myUid(), Process.myPid(), n, Process.myUserHandle(), null, 0); + } + @Test public void testSuggest_notMessageNotification() { Notification notification = mNotificationBuilder.setContentText(MESSAGE).build(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); @@ -137,7 +137,7 @@ public class SmartActionsHelperTest { .setContentText(MESSAGE) .setCategory(Notification.CATEGORY_MESSAGE) .build(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); ConversationActions.Request request = runSuggestAndCaptureRequest(); @@ -154,7 +154,7 @@ public class SmartActionsHelperTest { mSettings.mGenerateActions = false; mSettings.mGenerateReplies = false; Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); @@ -167,7 +167,7 @@ public class SmartActionsHelperTest { mSettings.mGenerateReplies = true; mSettings.mGenerateActions = false; Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); ConversationActions.Request request = runSuggestAndCaptureRequest(); @@ -184,7 +184,7 @@ public class SmartActionsHelperTest { mSettings.mGenerateReplies = false; mSettings.mGenerateActions = true; Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); ConversationActions.Request request = runSuggestAndCaptureRequest(); @@ -200,7 +200,7 @@ public class SmartActionsHelperTest { @Test public void testSuggest_nonMessageStyleMessageNotification() { Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); List<ConversationActions.Message> messages = runSuggestAndCaptureRequest().getConversation(); @@ -233,7 +233,7 @@ public class SmartActionsHelperTest { .setStyle(style) .setActions(createReplyAction()) .build(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); List<ConversationActions.Message> messages = runSuggestAndCaptureRequest().getConversation(); @@ -288,7 +288,7 @@ public class SmartActionsHelperTest { .setStyle(style) .setActions(createReplyAction()) .build(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); @@ -307,7 +307,7 @@ public class SmartActionsHelperTest { .setStyle(style) .setActions(createReplyAction()) .build(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); @@ -318,11 +318,11 @@ public class SmartActionsHelperTest { @Test public void testOnSuggestedReplySent() { Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); - mSmartActionsHelper.onSuggestedReplySent( - NOTIFICATION_KEY, SMART_REPLY, NotificationAssistantService.SOURCE_FROM_ASSISTANT); + mSmartActionsHelper.onSuggestedReplySent(mStatusBarNotification.getKey(), SMART_REPLY, + NotificationAssistantService.SOURCE_FROM_ASSISTANT); ArgumentCaptor<TextClassifierEvent> argumentCaptor = ArgumentCaptor.forClass(TextClassifierEvent.class); @@ -338,7 +338,7 @@ public class SmartActionsHelperTest { @Test public void testOnSuggestedReplySent_anotherNotification() { Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); mSmartActionsHelper.onSuggestedReplySent( @@ -353,11 +353,11 @@ public class SmartActionsHelperTest { when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class))) .thenReturn(new ConversationActions(Collections.singletonList(REPLY_ACTION), null)); Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); - mSmartActionsHelper.onSuggestedReplySent( - NOTIFICATION_KEY, SMART_REPLY, NotificationAssistantService.SOURCE_FROM_ASSISTANT); + mSmartActionsHelper.onSuggestedReplySent(mStatusBarNotification.getKey(), SMART_REPLY, + NotificationAssistantService.SOURCE_FROM_ASSISTANT); verify(mTextClassifier, never()).onTextClassifierEvent(any(TextClassifierEvent.class)); } @@ -365,10 +365,10 @@ public class SmartActionsHelperTest { @Test public void testOnNotificationDirectReply() { Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); - mSmartActionsHelper.onNotificationDirectReplied(NOTIFICATION_KEY); + mSmartActionsHelper.onNotificationDirectReplied(mStatusBarNotification.getKey()); ArgumentCaptor<TextClassifierEvent> argumentCaptor = ArgumentCaptor.forClass(TextClassifierEvent.class); @@ -381,7 +381,7 @@ public class SmartActionsHelperTest { @Test public void testOnNotificationExpansionChanged() { Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), true); @@ -397,7 +397,7 @@ public class SmartActionsHelperTest { @Test public void testOnNotificationsSeen_notExpanded() { Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), false); @@ -409,7 +409,7 @@ public class SmartActionsHelperTest { @Test public void testOnNotifications_expanded() { Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), true); @@ -438,7 +438,7 @@ public class SmartActionsHelperTest { Collections.singletonList(conversationAction), null)); Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); SmartActionsHelper.SmartSuggestions suggestions = mSmartActionsHelper.suggest(createNotificationEntry()); @@ -477,7 +477,8 @@ public class SmartActionsHelperTest { private NotificationEntry createNotificationEntry() { NotificationChannel channel = new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT); - return new NotificationEntry(mIPackageManager, mStatusBarNotification, channel, mSmsHelper); + return new NotificationEntry( + mContext, mIPackageManager, mStatusBarNotification, channel, mSmsHelper); } private Notification createMessageNotification() { diff --git a/packages/NetworkStack/AndroidManifestBase.xml b/packages/NetworkStack/AndroidManifestBase.xml index d00a55143605..3da566f88659 100644 --- a/packages/NetworkStack/AndroidManifestBase.xml +++ b/packages/NetworkStack/AndroidManifestBase.xml @@ -24,6 +24,7 @@ android:label="NetworkStack" android:defaultToDeviceProtectedStorage="true" android:directBootAware="true" + android:persistent="true" android:usesCleartextTraffic="true"> <service android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService" diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java index 0ffd471e64af..05c2f24001e8 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java @@ -174,7 +174,7 @@ public class Tile implements Parcelable { packageManager.getResourcesForApplication(mActivityPackage); title = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_TITLE)); } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { - Log.d(TAG, "Couldn't find info", e); + Log.w(TAG, "Couldn't find info", e); } } else { title = mMetaData.getString(META_DATA_PREFERENCE_TITLE); @@ -183,26 +183,16 @@ public class Tile implements Parcelable { // Set the preference title to the activity's label if no // meta-data is found if (title == null) { - title = getActivityInfo(context).loadLabel(packageManager); + final ActivityInfo activityInfo = getActivityInfo(context); + if (activityInfo == null) { + return null; + } + title = activityInfo.loadLabel(packageManager); } return title; } /** - * Returns the raw metadata for summary, this is used for comparing 2 summary text without - * loading the real string. - */ - public String getSummaryReference() { - if (mSummaryOverride != null) { - return mSummaryOverride.toString(); - } - if (mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) { - return mMetaData.get(META_DATA_PREFERENCE_SUMMARY).toString(); - } - return null; - } - - /** * Overrides the summary. This can happen when injected tile wants to provide dynamic summary. */ public void overrideSummary(CharSequence summaryOverride) { @@ -302,7 +292,7 @@ public class Tile implements Parcelable { if (iconResId != 0) { final Icon icon = Icon.createWithResource(activityInfo.packageName, iconResId); if (isIconTintable(context)) { - final TypedArray a = context.obtainStyledAttributes(new int[] { + final TypedArray a = context.obtainStyledAttributes(new int[]{ android.R.attr.colorControlNormal}); final int tintColor = a.getColor(0, 0); a.recycle(); @@ -357,6 +347,9 @@ public class Tile implements Parcelable { if (infoList != null && !infoList.isEmpty()) { mActivityInfo = infoList.get(0).activityInfo; mMetaData = mActivityInfo.metaData; + } else { + Log.e(TAG, "Cannot find package info for " + + intent.getComponent().flattenToString()); } } return mActivityInfo; diff --git a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml index 6b45a479836e..c8a80ac57c00 100644 --- a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml +++ b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml @@ -14,5 +14,5 @@ limitations under the License. --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:alpha="0.24" android:color="@android:color/black" /> + <item android:alpha="0.24" android:color="?android:attr/colorBackground" /> </selector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml index 5ef085b32dd6..8dcfdbb8cf1e 100644 --- a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml +++ b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml @@ -14,5 +14,5 @@ limitations under the License. --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:alpha="0.47" android:color="@android:color/black" /> + <item android:alpha="0.47" android:color="?android:attr/colorBackground" /> </selector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml index 81f63a48fb9a..34de5489a28b 100644 --- a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml +++ b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml @@ -14,5 +14,5 @@ limitations under the License. --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:alpha="0.3" android:color="@android:color/white" /> + <item android:alpha="0.3" android:color="?android:attr/colorForeground" /> </selector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml index 61f4e26cf00b..15944c3a2a07 100644 --- a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml +++ b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml @@ -14,5 +14,5 @@ limitations under the License. --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:color="@android:color/white" /> + <item android:color="?android:attr/colorForeground" /> </selector>
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index ff34578d0fea..1d351a5e53b6 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -50,6 +50,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> // See mConnectAttempted private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000; + // Some Hearing Aids (especially the 2nd device) needs more time to do service discovery + private static final long MAX_HEARING_AIDS_DELAY_FOR_AUTO_CONNECT = 15000; private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000; private final Context mContext; @@ -223,7 +225,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> // various profiles // If UUIDs are not available yet, connect will be happen // upon arrival of the ACTION_UUID intent. - Log.d(TAG, "No profiles. Maybe we will connect later"); + Log.d(TAG, "No profiles. Maybe we will connect later for device " + mDevice); return; } @@ -608,10 +610,12 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> long timeout = MAX_UUID_DELAY_FOR_AUTO_CONNECT; if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) { timeout = MAX_HOGP_DELAY_FOR_AUTO_CONNECT; + } else if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid)) { + timeout = MAX_HEARING_AIDS_DELAY_FOR_AUTO_CONNECT; } if (BluetoothUtils.D) { - Log.d(TAG, "onUuidChanged: Time since last connect" + Log.d(TAG, "onUuidChanged: Time since last connect=" + (SystemClock.elapsedRealtime() - mConnectAttempted)); } diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt index 257943e16149..a5b5312707d0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt +++ b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt @@ -44,6 +44,8 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) // doesn't touch the walls private val perimeterPath = Path() private val scaledPerimeter = Path() + private val errorPerimeterPath = Path() + private val scaledErrorPerimeter = Path() // Fill will cover the whole bounding rect of the fillMask, and be masked by the path private val fillMask = Path() private val scaledFill = Path() @@ -242,7 +244,7 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) } } else if (powerSaveEnabled) { // If power save is enabled draw the perimeter path with colorError - c.drawPath(scaledPerimeter, errorPaint) + c.drawPath(scaledErrorPerimeter, errorPaint) // And draw the plus sign on top of the fill c.drawPath(scaledPlus, errorPaint) } @@ -364,6 +366,7 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) } perimeterPath.transform(scaleMatrix, scaledPerimeter) + errorPerimeterPath.transform(scaleMatrix, scaledErrorPerimeter) fillMask.transform(scaleMatrix, scaledFill) scaledFill.computeBounds(fillRect, true) boltPath.transform(scaleMatrix, scaledBolt) @@ -382,8 +385,12 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) val pathString = context.resources.getString( com.android.internal.R.string.config_batterymeterPerimeterPath) perimeterPath.set(PathParser.createPathFromPathData(pathString)) - val b = RectF() - perimeterPath.computeBounds(b, true) + perimeterPath.computeBounds(RectF(), true) + + val errorPathString = context.resources.getString( + com.android.internal.R.string.config_batterymeterErrorPerimeterPath) + errorPerimeterPath.set(PathParser.createPathFromPathData(errorPathString)) + errorPerimeterPath.computeBounds(RectF(), true) val fillMaskString = context.resources.getString( com.android.internal.R.string.config_batterymeterFillMask) diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index 5f2bc4e1d490..d8172a0f9430 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -123,7 +123,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro /** * Synchronization lock for managing concurrency between main and worker threads. * - * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints}. + * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints} and + * {@link #mScanner}. */ private final Object mLock = new Object(); @@ -168,6 +169,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro private static final String WIFI_SECURITY_OWE = "OWE"; private static final String WIFI_SECURITY_SUITE_B_192 = "SUITE_B_192"; + @GuardedBy("mLock") @VisibleForTesting Scanner mScanner; @@ -276,9 +278,11 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro * <p>Sets {@link #mStaleScanResults} to true. */ private void pauseScanning() { - if (mScanner != null) { - mScanner.pause(); - mScanner = null; + synchronized (mLock) { + if (mScanner != null) { + mScanner.pause(); + mScanner = null; + } } mStaleScanResults = true; } @@ -289,12 +293,14 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro * <p>The score cache should be registered before this method is invoked. */ public void resumeScanning() { - if (mScanner == null) { - mScanner = new Scanner(); - } + synchronized (mLock) { + if (mScanner == null) { + mScanner = new Scanner(); + } - if (isWifiEnabled()) { - mScanner.resume(); + if (isWifiEnabled()) { + mScanner.resume(); + } } } @@ -743,7 +749,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } private void updateNetworkInfo(NetworkInfo networkInfo) { - /* Sticky broadcasts can call this when wifi is disabled */ if (!isWifiEnabled()) { clearAccessPointsAndConditionallyUpdate(); @@ -880,18 +885,25 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro * true. */ private void updateWifiState(int state) { + if (isVerboseLoggingEnabled()) { + Log.d(TAG, "updateWifiState: " + state); + } if (state == WifiManager.WIFI_STATE_ENABLED) { - if (mScanner != null) { - // We only need to resume if mScanner isn't null because - // that means we want to be scanning. - mScanner.resume(); + synchronized (mLock) { + if (mScanner != null) { + // We only need to resume if mScanner isn't null because + // that means we want to be scanning. + mScanner.resume(); + } } } else { clearAccessPointsAndConditionallyUpdate(); mLastInfo = null; mLastNetworkInfo = null; - if (mScanner != null) { - mScanner.pause(); + synchronized (mLock) { + if (mScanner != null) { + mScanner.pause(); + } } mStaleScanResults = true; } @@ -919,12 +931,18 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro private int mRetry = 0; void resume() { + if (isVerboseLoggingEnabled()) { + Log.d(TAG, "Scanner resume"); + } if (!hasMessages(MSG_SCAN)) { sendEmptyMessage(MSG_SCAN); } } void pause() { + if (isVerboseLoggingEnabled()) { + Log.d(TAG, "Scanner pause"); + } mRetry = 0; removeMessages(MSG_SCAN); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java index d0d1e58effaa..5da6205d45bd 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java @@ -23,6 +23,7 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowPackageManager; +import org.robolectric.util.ReflectionHelpers; @RunWith(RobolectricTestRunner.class) public class TileTest { @@ -164,4 +165,17 @@ public class TileTest { assertThat(tile.mLastUpdateTime).isNotEqualTo(staleTimeStamp); } + + @Test + public void getTitle_noActivity_returnNull() { + final ResolveInfo info = new ResolveInfo(); + info.activityInfo = mActivityInfo; + final ShadowPackageManager spm = Shadow.extract(mContext.getPackageManager()); + spm.removePackage(mActivityInfo.packageName); + + final Tile tile = new Tile(mActivityInfo, "category"); + ReflectionHelpers.setField(tile, "mActivityInfo", null); + + assertThat(tile.getTitle(RuntimeEnvironment.application)).isNull(); + } } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java index 66d5d1160bf6..25a3fa2f5f20 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java @@ -17,6 +17,7 @@ package com.android.systemui.plugins; import android.annotation.Nullable; import android.app.PendingIntent; import android.content.Intent; +import android.view.View; import com.android.systemui.plugins.annotations.ProvidesInterface; @@ -32,13 +33,20 @@ public interface ActivityStarter { void startPendingIntentDismissingKeyguard(PendingIntent intent); /** - * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but allows + * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent)}, but allows * you to specify the callback that is executed on the UI thread after the intent is sent. */ void startPendingIntentDismissingKeyguard(PendingIntent intent, Runnable intentSentUiThreadCallback); /** + * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but also + * specifies an associated view that should be used for the activity launch animation. + */ + void startPendingIntentDismissingKeyguard(PendingIntent intent, + Runnable intentSentUiThreadCallback, View associatedView); + + /** * The intent flag can be specified in startActivity(). */ void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags); diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActionsPanelPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActionsPanelPlugin.java index 6be770788ce1..68d2ed7d4689 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActionsPanelPlugin.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActionsPanelPlugin.java @@ -26,8 +26,8 @@ import com.android.systemui.plugins.annotations.ProvidesInterface; * Plugin which provides a "Panel" {@link View} to be rendered inside of the GlobalActions menu. * * Implementations should construct a new {@link PanelViewController} with the given - * {@link Callbacks} instance inside of {@link #onPanelShown(Callbacks)}, and should not hold onto - * a reference, instead allowing Global Actions to manage the lifetime of the object. + * {@link Callbacks} instance inside of {@link #onPanelShown(Callbacks, boolean)}, and should not + * hold onto a reference, instead allowing Global Actions to manage the lifetime of the object. * * Under this assumption, {@link PanelViewController} represents the lifetime of a single invocation * of the Global Actions menu. The {@link View} for the Panel is generated when the @@ -50,9 +50,10 @@ public interface GlobalActionsPanelPlugin extends Plugin { * * @param callbacks {@link Callbacks} instance that can be used by the Panel to interact with * the Global Actions menu. + * @param deviceLocked Indicates whether or not the device is currently locked. * @return A {@link PanelViewController} instance used to receive Global Actions events. */ - PanelViewController onPanelShown(Callbacks callbacks); + PanelViewController onPanelShown(Callbacks callbacks, boolean deviceLocked); /** * Provides methods to interact with the Global Actions menu. @@ -92,5 +93,10 @@ public interface GlobalActionsPanelPlugin extends Plugin { * {@link #getPanelContent()}) is dismissed. */ void onDismissed(); + + /** + * Invoked when the device is either locked or unlocked. + */ + void onDeviceLockStateChanged(boolean locked); } } diff --git a/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_left_animation.xml b/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_left_animation.xml deleted file mode 100644 index b5dd5c359a9e..000000000000 --- a/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_left_animation.xml +++ /dev/null @@ -1,77 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- 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. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <set - android:ordering="sequentially" > - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="233" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="233" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="233" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="233" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - </set> -</set> diff --git a/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_right_animation.xml b/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_right_animation.xml deleted file mode 100644 index 14704bf3da5d..000000000000 --- a/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_right_animation.xml +++ /dev/null @@ -1,77 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- 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. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <set - android:ordering="sequentially" > - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="233" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="233" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="233" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="233" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - </set> -</set> diff --git a/packages/SystemUI/res/anim/ic_caret_down_left_animation.xml b/packages/SystemUI/res/anim/ic_caret_down_left_animation.xml deleted file mode 100644 index d465f198a679..000000000000 --- a/packages/SystemUI/res/anim/ic_caret_down_left_animation.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - 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. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <objectAnimator - android:duration="250" - android:propertyXName="translateX" - android:propertyYName="translateY" - android:pathData="M 12.699,8.701 c 0.0,1.09767 0.0,5.48833 0.0,6.586" - android:interpolator="@android:interpolator/fast_out_slow_in" /> - <objectAnimator - android:duration="200" - android:propertyName="rotation" - android:valueFrom="-45.0" - android:valueTo="45.0" - android:valueType="floatType" - android:interpolator="@interpolator/ic_caret_down_animation_interpolator_0" /> -</set> diff --git a/packages/SystemUI/res/anim/ic_caret_down_right_animation.xml b/packages/SystemUI/res/anim/ic_caret_down_right_animation.xml deleted file mode 100644 index 7a38ac323242..000000000000 --- a/packages/SystemUI/res/anim/ic_caret_down_right_animation.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - 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. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <objectAnimator - android:duration="250" - android:propertyXName="translateX" - android:propertyYName="translateY" - android:pathData="M 11.287,8.701 c 0.0,1.09767 0.0,5.48833 0.0,6.586" - android:interpolator="@android:interpolator/fast_out_slow_in" /> - <objectAnimator - android:duration="200" - android:propertyName="rotation" - android:valueFrom="45.0" - android:valueTo="-45.0" - android:valueType="floatType" - android:interpolator="@interpolator/ic_caret_down_animation_interpolator_0" /> -</set> diff --git a/packages/SystemUI/res/anim/ic_caret_up_left_animation.xml b/packages/SystemUI/res/anim/ic_caret_up_left_animation.xml deleted file mode 100644 index 125a6164bfd1..000000000000 --- a/packages/SystemUI/res/anim/ic_caret_up_left_animation.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - 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. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <objectAnimator - android:duration="250" - android:propertyXName="translateX" - android:propertyYName="translateY" - android:pathData="M 12.699,15.287 c -0.04833,0.452 -0.04833,-7.03583 0.0,-6.586" - android:interpolator="@android:interpolator/fast_out_slow_in" /> - <objectAnimator - android:duration="200" - android:propertyName="rotation" - android:valueFrom="45.0" - android:valueTo="-45.0" - android:valueType="floatType" - android:interpolator="@interpolator/ic_caret_up_animation_interpolator_0" /> -</set> diff --git a/packages/SystemUI/res/anim/ic_caret_up_right_animation.xml b/packages/SystemUI/res/anim/ic_caret_up_right_animation.xml deleted file mode 100644 index 3fd4df58f51c..000000000000 --- a/packages/SystemUI/res/anim/ic_caret_up_right_animation.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - 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. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <objectAnimator - android:duration="250" - android:propertyXName="translateX" - android:propertyYName="translateY" - android:pathData="M 11.287,15.287 c 0.04883,0.452 0.04883,-7.03583 0.0,-6.586" - android:interpolator="@android:interpolator/fast_out_slow_in" /> - <objectAnimator - android:duration="200" - android:propertyName="rotation" - android:valueFrom="-45.0" - android:valueTo="45.0" - android:valueType="floatType" - android:interpolator="@interpolator/ic_caret_up_animation_interpolator_0" /> -</set> diff --git a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_1_animation.xml b/packages/SystemUI/res/anim/ic_hotspot_transient_circle_1_animation.xml deleted file mode 100644 index 5e7184700614..000000000000 --- a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_1_animation.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - 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. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <set - android:ordering="sequentially" > - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="633" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - </set> -</set> diff --git a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_2_animation.xml b/packages/SystemUI/res/anim/ic_hotspot_transient_circle_2_animation.xml deleted file mode 100644 index 9081e6f48f11..000000000000 --- a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_2_animation.xml +++ /dev/null @@ -1,50 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - 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. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <set - android:ordering="sequentially" > - <objectAnimator - android:duration="200" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="633" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - </set> -</set> diff --git a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_3_animation.xml b/packages/SystemUI/res/anim/ic_hotspot_transient_circle_3_animation.xml deleted file mode 100644 index eaf5f05b8863..000000000000 --- a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_3_animation.xml +++ /dev/null @@ -1,50 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - 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. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <set - android:ordering="sequentially" > - <objectAnimator - android:duration="400" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="633" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - </set> -</set> diff --git a/packages/SystemUI/res/anim/ic_signal_wifi_transient_wifi_1_animation.xml b/packages/SystemUI/res/anim/ic_signal_wifi_transient_wifi_1_animation.xml deleted file mode 100644 index 919a4ddd4f08..000000000000 --- a/packages/SystemUI/res/anim/ic_signal_wifi_transient_wifi_1_animation.xml +++ /dev/null @@ -1,66 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <set - android:ordering="sequentially" > - <objectAnimator - android:duration="333" - android:propertyName="pathData" - android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueType="pathType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="pathData" - android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueType="pathType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="316" - android:propertyName="pathData" - android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueType="pathType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="pathData" - android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueType="pathType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="316" - android:propertyName="pathData" - android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueType="pathType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="pathData" - android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueType="pathType" - android:interpolator="@android:interpolator/linear" /> - </set> - <set - android:ordering="sequentially" > - <objectAnimator - android:duration="333" - android:propertyName="fillAlpha" - android:valueFrom="0.0" - android:valueTo="0.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="0.0" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - </set> -</set> diff --git a/packages/SystemUI/res/drawable/button_border_selected.xml b/packages/SystemUI/res/drawable/button_border_selected.xml index 1e40adefca2a..01e7099844a9 100644 --- a/packages/SystemUI/res/drawable/button_border_selected.xml +++ b/packages/SystemUI/res/drawable/button_border_selected.xml @@ -19,7 +19,7 @@ <solid android:color="@color/notification_guts_selection_bg" /> <stroke - android:width="2dp" + android:width="1dp" android:color="@color/GM2_grey_300"/> <corners android:radius="@dimen/rect_button_radius" /> </shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/button_border_unselected.xml b/packages/SystemUI/res/drawable/button_border_unselected.xml index 4ea37640780f..b9c4ced3631b 100644 --- a/packages/SystemUI/res/drawable/button_border_unselected.xml +++ b/packages/SystemUI/res/drawable/button_border_unselected.xml @@ -18,7 +18,7 @@ android:shape="rectangle" android:color="@color/notification_guts_selection_bg"> <stroke - android:width="2dp" + android:width="1dp" android:color="@color/GM2_grey_300"/> <corners android:radius="@dimen/rect_button_radius" /> diff --git a/packages/SystemUI/res/drawable/ic_bluetooth_transient.xml b/packages/SystemUI/res/drawable/ic_bluetooth_transient.xml deleted file mode 100644 index 33d1fb3fbe2b..000000000000 --- a/packages/SystemUI/res/drawable/ic_bluetooth_transient.xml +++ /dev/null @@ -1,76 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- 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. ---> -<vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:name="ic_bluetooth_transient" - android:width="48dp" - android:viewportWidth="48" - android:height="48dp" - android:viewportHeight="48" > - <group - android:name="ic_bluetooth_transient_0" - android:translateX="23.99883" - android:translateY="23.99839" > - <group - android:name="ic_bluetooth_transient_pivot" - android:translateX="-23.99883" - android:translateY="-23.99839" > - <group - android:name="ic_bluetooth_white_outlines" - android:translateX="22.73986" - android:translateY="23.99839" > - <group - android:name="ic_bluetooth_white_outlines_pivot" - android:translateX="-12.84937" - android:translateY="-20.4772" > - <path - android:name="b_shape_merged" - android:pathData="M 17.1289978027,20.4790039062 c 0.0,0.0 7.5,-7.48100280762 7.5,-7.48100280762 c 0.81999206543,-0.819000244141 0.81999206543,-2.13899230957 0.0,-2.95999145508 c 0.0,0.0 -8.93899536133,-8.93899536133 -8.93899536133,-8.93899536133 c 0.0,0.0 -0.0610046386719,-0.0610046386719 -0.0610046386718,-0.0610046386719 c -0.844009399414,-0.788009643555 -2.16799926758,-0.74299621582 -2.95600891113,0.102005004883 c -0.359985351562,0.384994506836 -0.561996459961,0.891998291016 -0.56298828125,1.41899108887 c 0.0,0.0 0.0,12.8800048828 0.0,12.8800048828 c 0.0,0.0 -8.10000610352,-8.10000610352 -8.10000610352,-8.10000610352 c -0.81999206543,-0.81999206543 -2.12100219727,-0.81999206543 -2.9409942627,0.0 c -0.819000244141,0.819000244141 -0.819000244141,2.12001037598 0.0,2.94000244141 c 0.0,0.0 10.1799926758,10.1999969482 10.1799926758,10.1999969482 c 0.0,0.0 -10.1799926758,10.1790008545 -10.1799926758,10.1790008545 c -0.819000244141,0.820999145508 -0.819000244141,2.12100219727 0.0,2.94100952148 c 0.81999206543,0.81999206543 2.12100219727,0.81999206543 2.9409942627,0.0 c 0.0,0.0 8.10000610352,-8.1009979248 8.10000610352,-8.1009979248 c 0.0,0.0 0.0,12.9009857178 0.0,12.9009857178 c 0.0,1.14801025391 0.929992675781,2.08000183105 2.08000183105,2.08000183105 c 0.526992797852,0.0 1.03399658203,-0.199996948242 1.41999816895,-0.559997558594 c 0.0,0.0 0.0989990234375,-0.100006103516 0.0989990234375,-0.100006103516 c 0.0,0.0 8.91999816895,-8.91999816895 8.91999816895,-8.91999816895 c 0.81999206543,-0.820999145508 0.81999206543,-2.13999938965 0.0,-2.95999145508 c 0.0,0.0 -7.5,-7.46000671387 -7.5,-7.46000671387 Z M 16.0899963379,15.8190002441 c 0.0,0.0 0.0,-8.59999084473 0.0,-8.59999084473 c 0.0,0.0 4.30000305176,4.30000305176 4.30000305176,4.30000305176 c 0.0,0.0 -4.30000305176,4.29998779297 -4.30000305176,4.29998779297 Z M 16.0899963379,33.7190093994 c 0.0,0.0 0.0,-8.6009979248 0.0,-8.6009979248 c 0.0,0.0 4.30000305176,4.30099487305 4.30000305176,4.30099487305 c 0.0,0.0 -4.30000305176,4.30000305176 -4.30000305176,4.30000305176 Z" - android:fillColor="#FFFFFFFF" /> - </group> - </group> - <group - android:name="dot_left_outlines" - android:translateX="20.6501" - android:translateY="24.00011" > - <group - android:name="dot_left_outlines_pivot" - android:translateX="-14.2" - android:translateY="-3.55" > - <path - android:name="dot_left" - android:pathData="M 5.66999816895,5.66999816895 c -1.18000793457,1.17999267578 -3.08000183105,1.17999267578 -4.24000549316,0.0 c -1.17999267578,-1.16000366211 -1.17999267578,-3.03999328613 -0.0199890136719,-4.2200012207 c 0.0,0.0 0.0199890136719,-0.0200042724609 0.0199890136719,-0.0200042724609 c 1.16000366211,-1.17999267578 3.04000854492,-1.17999267578 4.2200012207,-0.0199890136719 c 0.0,0.0 0.0200042724609,0.0199890136719 0.0200042724609,0.0199890136719 c 1.17999267578,1.17900085449 1.17999267578,3.06001281738 0.0,4.24000549316 Z" - android:fillColor="#FFFFFFFF" - android:fillAlpha="0.5" /> - </group> - </group> - <group - android:name="dot_right_outlines" - android:translateX="27.3501" - android:translateY="23.99741" > - <group - android:name="dot_right_outlines_pivot" - android:translateX="7.1" - android:translateY="-3.5525" > - <path - android:name="dot_right" - android:pathData="M 5.66999816895,1.43499755859 c 1.17999267578,1.18000793457 1.17999267578,3.08000183105 0.0,4.24000549316 c -1.18000793457,1.17999267578 -3.08000183105,1.17999267578 -4.24000549316,0.0 c -1.17999267578,-1.16000366211 -1.17999267578,-3.04000854492 -0.0200042724609,-4.21899414062 c 0.0,0.0 0.0200042724609,-0.0210113525391 0.0200042724609,-0.0210113525391 c 1.15299987793,-1.17098999023 3.03799438477,-1.18499755859 4.20899963379,-0.0309906005859 c 0.0,0.0 0.031005859375,0.0309906005859 0.031005859375,0.0309906005859 Z" - android:fillColor="#FFFFFFFF" /> - </group> - </group> - </group> - </group> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_hotspot_transient.xml b/packages/SystemUI/res/drawable/ic_hotspot_transient.xml deleted file mode 100644 index 22f72675ffc0..000000000000 --- a/packages/SystemUI/res/drawable/ic_hotspot_transient.xml +++ /dev/null @@ -1,76 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - 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. ---> -<vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:name="ic_hotspot_transient" - android:width="48dp" - android:viewportWidth="48" - android:height="48dp" - android:viewportHeight="48" > - <group - android:name="ic_hotspot_transient_0" - android:translateX="24.00209" - android:translateY="24.2354" > - <group - android:name="ic_hotspot_transient_pivot" - android:translateX="-24.00209" - android:translateY="-24.2354" > - <group - android:name="circle01" - android:translateX="24.0002" - android:translateY="25.9996" > - <group - android:name="circle01_pivot" - android:translateX="-24.0002" - android:translateY="-25.9996" > - <path - android:name="circle01_0" - android:pathData="M 24.0001983643,21.9996032715 c -2.19999694824,0.0 -4.0,1.80000305176 -4.0,4.0 c 0.0,2.20100402832 1.80000305176,4.0 4.0,4.0 c 2.19999694824,0.0 4.0,-1.79899597168 4.0,-4.0 c 0.0,-2.19999694824 -1.80000305176,-4.0 -4.0,-4.0 Z" - android:fillColor="#FFFFFFFF" /> - </group> - </group> - <group - android:name="circle02" - android:translateX="24.00071" - android:translateY="24.74686" > - <group - android:name="circle02_pivot" - android:translateX="-24.00071" - android:translateY="-24.74686" > - <path - android:name="circle02_0" - android:pathData="M 36.0001983643,25.9996032715 c -0.00299072265625,-6.62699890137 -5.37899780273,-11.9969940186 -12.0059967041,-11.9940032959 c -0.5,0.0 -0.999008178711,0.031005859375 -1.4940032959,0.0940093994141 c -5.24000549316,0.640991210938 -9.55999755859,4.81999206543 -10.3600006104,10.0399932861 c -0.639999389648,4.2799987793 0.979995727539,8.22099304199 3.83999633789,10.7799987793 c 0.960006713867,0.86100769043 2.48001098633,0.660003662109 3.1190032959,-0.460006713867 c 0.481002807617,-0.839996337891 0.281005859375,-1.87998962402 -0.438995361328,-2.51899719238 c -2.21299743652,-1.97099304199 -3.15200805664,-5.00500488281 -2.44000244141,-7.8809967041 c 0.695007324219,-2.86999511719 2.93099975586,-5.11500549316 5.79899597168,-5.81900024414 c 4.29100036621,-1.08599853516 8.65000915527,1.51100158691 9.73600769043,5.80200195312 c 0.162002563477,0.639999389648 0.244003295898,1.29699707031 0.244995117188,1.95700073242 c 0.0,2.36099243164 -1.02000427246,4.45999145508 -2.66000366211,5.91999816895 c -0.720001220703,0.660003662109 -0.939987182617,1.69999694824 -0.459991455078,2.53999328613 c 0.619995117188,1.08000183105 2.08000183105,1.38000488281 3.0,0.560012817383 c 2.61599731445,-2.26699829102 4.1190032959,-5.55801391602 4.11999511719,-9.02000427246 Z" - android:fillColor="#FFFFFFFF" /> - </group> - </group> - <group - android:name="circle03" - android:translateX="24.00209" - android:translateY="24.23539" > - <group - android:name="circle03_pivot" - android:translateX="-24.00209" - android:translateY="-24.23539" > - <path - android:name="circle03_0" - android:pathData="M 21.6602020264,6.13960266113 c -9.24000549316,1.03999328613 -16.6999969482,8.66000366211 -17.5599975586,17.9199981689 c -0.690002441406,7.00500488281 2.36599731445,13.8540039062 8.03999328613,18.0200042725 c 0.958999633789,0.700988769531 2.32000732422,0.401000976562 2.91900634766,-0.620010375977 c 0.5,-0.860000610352 0.280990600586,-1.97898864746 -0.518997192383,-2.57998657227 c -4.56001281738,-3.38000488281 -7.30000305176,-9.09901428223 -6.32000732422,-15.3990020752 c 1.08000183105,-7.0 6.91900634766,-12.5800018311 13.9600067139,-13.3610076904 c 9.63999938965,-1.09999084473 17.8199920654,6.44000244141 17.8199920654,15.8800048828 c 0.0,5.30000305176 -2.57998657227,9.95999145508 -6.53999328613,12.8800048828 c -0.800003051758,0.600997924805 -1.02000427246,1.69999694824 -0.520004272461,2.57998657227 c 0.600006103516,1.04000854492 1.96000671387,1.32099914551 2.91999816895,0.620010375977 c 5.12100219727,-3.75500488281 8.14500427246,-9.72799682617 8.13999938965,-16.0800018311 c 0.0,-11.8200073242 -10.2599945068,-21.2400054932 -22.3399963379,-19.8600006104 Z" - android:fillColor="#FFFFFFFF" /> - </group> - </group> - </group> - </group> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_hotspot_transient_animation.xml b/packages/SystemUI/res/drawable/ic_hotspot_transient_animation.xml deleted file mode 100644 index 929a941164a6..000000000000 --- a/packages/SystemUI/res/drawable/ic_hotspot_transient_animation.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - 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. ---> -<animated-vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:drawable="@drawable/ic_hotspot_transient" > - <target - android:name="circle01_0" - android:animation="@anim/ic_hotspot_transient_circle_1_animation" /> - <target - android:name="circle02_0" - android:animation="@anim/ic_hotspot_transient_circle_2_animation" /> - <target - android:name="circle03_0" - android:animation="@anim/ic_hotspot_transient_circle_3_animation" /> -</animated-vector> diff --git a/packages/SystemUI/res/drawable/ic_signal_wifi_transient.xml b/packages/SystemUI/res/drawable/ic_signal_wifi_transient.xml deleted file mode 100644 index 880e1bb075c7..000000000000 --- a/packages/SystemUI/res/drawable/ic_signal_wifi_transient.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:name="ic_signal_wifi_transient" - android:width="48dp" - android:viewportWidth="48" - android:height="48dp" - android:viewportHeight="48" > - <group - android:name="ic_signal_wifi_4_bar_48px_2" - android:translateX="24.25" - android:translateY="25.73401" > - <group - android:name="ic_signal_wifi_4_bar_48px_2_pivot" - android:translateX="-23.21545" - android:translateY="-18.86649" > - <group - android:name="wifi_2" - android:translateX="23.481" - android:translateY="18.71151" > - <path - android:name="wifi" - android:pathData="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:fillColor="#FFFFFFFF" - android:fillAlpha="0.5" /> - </group> - <group - android:name="wifi_0" - android:translateX="23.481" - android:translateY="18.71151" > - <path - android:name="wifi_1" - android:pathData="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:fillColor="#FFFFFFFF" - android:fillAlpha="0" /> - </group> - </group> - </group> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/SystemUI/res/drawable/ic_signal_wifi_transient_animation.xml deleted file mode 100644 index 9f5aaebaf650..000000000000 --- a/packages/SystemUI/res/drawable/ic_signal_wifi_transient_animation.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<animated-vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:drawable="@drawable/ic_signal_wifi_transient" > - <target - android:name="wifi_1" - android:animation="@anim/ic_signal_wifi_transient_wifi_1_animation" /> -</animated-vector> diff --git a/packages/SystemUI/res/drawable/ic_volume_collapse.xml b/packages/SystemUI/res/drawable/ic_volume_collapse.xml deleted file mode 100644 index 3853d121ce2c..000000000000 --- a/packages/SystemUI/res/drawable/ic_volume_collapse.xml +++ /dev/null @@ -1,55 +0,0 @@ -<!-- - Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:name="ic_caret_down" - android:width="24dp" - android:viewportWidth="24" - android:height="24dp" - android:viewportHeight="24" - android:tint="?android:attr/colorControlNormal" > - <group - android:name="caret___4" > - <group - android:name="right" - android:translateX="11.287" - android:translateY="8.701" - android:rotation="45" > - <group - android:name="right_pivot" - android:translateX="4.242" > - <path - android:name="rectangle_path_1" - android:fillColor="#FFFFFFFF" - android:pathData="M -3.242,-1.0 l 6.484,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l -6.484,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" /> - </group> - </group> - <group - android:name="left" - android:translateX="12.699" - android:translateY="8.701" - android:rotation="-45" > - <group - android:name="left_pivot" - android:translateX="-4.242" > - <path - android:name="rectangle_path_2" - android:fillColor="#FFFFFFFF" - android:pathData="M -3.242,-1.0 l 6.484,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l -6.484,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" /> - </group> - </group> - </group> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml b/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml index 18c6307b9ad9..101a0c89857c 100644 --- a/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml +++ b/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml @@ -13,13 +13,223 @@ See the License for the specific language governing permissions and limitations under the License. --> -<animated-vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:drawable="@drawable/ic_volume_collapse" > - <target - android:name="right" - android:animation="@anim/ic_caret_down_right_animation" /> - <target - android:name="left" - android:animation="@anim/ic_caret_down_left_animation" /> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="android:drawable"> + <vector android:height="24dp" android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_1_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_1_G_T_1" android:translateX="-0.004" + android:translateY="-3.608" android:rotation="0"> + <group android:name="_R_G_L_1_G" android:translateX="-0.246" + android:translateY="-1.642"> + <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c "/> + </group> + </group> + </group> + <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_0_G_T_1" android:translateX="0" + android:translateY="-3.585" android:rotation="0"> + <group android:name="_R_G_L_0_G" android:translateX="-8.25" + android:translateY="-1.665"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c "/> + </group> + </group> + </group> + </group> + <group android:name="time_group"/> + </vector> + </aapt:attr> + <target android:name="_R_G_L_1_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" android:duration="50" + android:startOffset="0" + android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c " + android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.96,0.9 0.96,0.9 C0.96,0.9 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="33" + android:startOffset="50" + android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.96,0.9 0.96,0.9 C0.96,0.9 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c " + android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.96,0.9 0.96,0.9 C0.96,0.9 -0.55,2.35 -0.55,2.35 C-0.55,2.35 6.84,9.66 6.84,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="83" + android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.96,0.9 0.96,0.9 C0.96,0.9 -0.55,2.35 -0.55,2.35 C-0.55,2.35 6.84,9.66 6.84,9.66c " + android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -0.87,2.12 -0.87,2.12 C-0.87,2.12 6.84,9.66 6.84,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="33" + android:startOffset="100" + android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -0.87,2.12 -0.87,2.12 C-0.87,2.12 6.84,9.66 6.84,9.66c " + android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.05,1.92 -1.05,1.92 C-1.05,1.92 6.84,9.66 6.84,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="133" + android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.05,1.92 -1.05,1.92 C-1.05,1.92 6.84,9.66 6.84,9.66c " + android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateXY" android:duration="250" + android:startOffset="0" android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M -0.004,-3.608C -0.004,-2.39758332538605 -0.004,2.44358332538605 -0.004,3.654"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="rotation" android:duration="200" + android:startOffset="0" android:valueFrom="0" android:valueTo="-90" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" android:duration="50" + android:startOffset="0" + android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c " + android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.2,0.59 8.2,0.59 C8.2,0.59 9.84,1.88 9.84,1.88 C9.84,1.88 1.66,9.66 1.66,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="50" + android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.2,0.59 8.2,0.59 C8.2,0.59 9.84,1.88 9.84,1.88 C9.84,1.88 1.66,9.66 1.66,9.66c " + android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="67" + android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c " + android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 7.45,0.93 7.45,0.93 C7.45,0.93 9.23,2.03 9.23,2.03 C9.23,2.03 1.66,9.66 1.66,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="83" + android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 7.45,0.93 7.45,0.93 C7.45,0.93 9.23,2.03 9.23,2.03 C9.23,2.03 1.66,9.66 1.66,9.66c " + android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.37,1.99 9.37,1.99 C9.37,1.99 1.66,9.66 1.66,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="33" + android:startOffset="100" + android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.37,1.99 9.37,1.99 C9.37,1.99 1.66,9.66 1.66,9.66c " + android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="133" + android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c " + android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateXY" android:duration="250" + android:startOffset="0" android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 0,-3.585C 0,-2.3787500476837202 0,2.4457500476837204 0,3.652"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="rotation" android:duration="200" + android:startOffset="0" android:valueFrom="0" android:valueTo="90" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateX" android:duration="267" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> + </set> + </aapt:attr> + </target> </animated-vector> diff --git a/packages/SystemUI/res/drawable/ic_volume_expand.xml b/packages/SystemUI/res/drawable/ic_volume_expand.xml deleted file mode 100644 index 79ff80806438..000000000000 --- a/packages/SystemUI/res/drawable/ic_volume_expand.xml +++ /dev/null @@ -1,55 +0,0 @@ -<!-- - Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:name="ic_caret_up" - android:width="24dp" - android:viewportWidth="24" - android:height="24dp" - android:viewportHeight="24" - android:tint="?android:attr/colorControlNormal" > - <group - android:name="caret___4" > - <group - android:name="right" - android:translateX="11.287" - android:translateY="15.287" - android:rotation="-45" > - <group - android:name="right_pivot" - android:translateX="4.242" > - <path - android:name="rectangle_path_1" - android:fillColor="#FFFFFFFF" - android:pathData="M -3.242,-1.0 l 6.484,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l -6.484,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" /> - </group> - </group> - <group - android:name="left" - android:translateX="12.699" - android:translateY="15.287" - android:rotation="45" > - <group - android:name="left_pivot" - android:translateX="-4.242" > - <path - android:name="rectangle_path_2" - android:fillColor="#FFFFFFFF" - android:pathData="M -3.242,-1.0 l 6.484,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l -6.484,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" /> - </group> - </group> - </group> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml b/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml index abd6678e335f..152d26c8c1d7 100644 --- a/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml +++ b/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml @@ -13,13 +13,233 @@ See the License for the specific language governing permissions and limitations under the License. --> -<animated-vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:drawable="@drawable/ic_volume_expand" > - <target - android:name="right" - android:animation="@anim/ic_caret_up_right_animation" /> - <target - android:name="left" - android:animation="@anim/ic_caret_up_left_animation" /> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="android:drawable"> + <vector android:height="24dp" android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_1_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_1_G_T_1" android:translateX="0" + android:translateY="3.439" android:rotation="0"> + <group android:name="_R_G_L_1_G" android:translateX="-8.25" + android:translateY="-8.1"> + <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 8.25,9.66 8.25,9.66 C8.25,9.66 8.25,6.83 8.25,6.83 C8.25,6.83 1.66,0.25 1.66,0.25c "/> + </group> + </group> + </group> + <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_0_G_T_1" android:translateX="0" + android:translateY="3.439" android:rotation="0"> + <group android:name="_R_G_L_0_G" android:translateX="-0.25" + android:translateY="-8.1"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.25,9.66 0.25,9.66 C0.25,9.66 0.25,6.83 0.25,6.83 C0.25,6.83 6.84,0.25 6.84,0.25c "/> + </group> + </group> + </group> + </group> + <group android:name="time_group"/> + </vector> + </aapt:attr> + <target android:name="_R_G_L_1_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" android:duration="33" + android:startOffset="0" + android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 8.25,9.66 8.25,9.66 C8.25,9.66 8.25,6.83 8.25,6.83 C8.25,6.83 1.66,0.25 1.66,0.25c " + android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.97,9.5 7.97,9.5 C7.97,9.5 8.59,7.15 8.59,7.15 C8.59,7.15 1.66,0.25 1.66,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="33" + android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.97,9.5 7.97,9.5 C7.97,9.5 8.59,7.15 8.59,7.15 C8.59,7.15 1.66,0.25 1.66,0.25c " + android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.71,9.4 7.71,9.4 C7.71,9.4 8.59,7.15 8.59,7.15 C8.59,7.15 1.66,0.25 1.66,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="50" + android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.71,9.4 7.71,9.4 C7.71,9.4 8.59,7.15 8.59,7.15 C8.59,7.15 1.66,0.25 1.66,0.25c " + android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 8.87,7.47 8.87,7.47 C8.87,7.47 1.66,0.25 1.66,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="67" + android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 8.87,7.47 8.87,7.47 C8.87,7.47 1.66,0.25 1.66,0.25c " + android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 9,7.68 9,7.68 C9,7.68 1.66,0.25 1.66,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="83" + android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 9,7.68 9,7.68 C9,7.68 1.66,0.25 1.66,0.25c " + android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 9.08,7.81 9.08,7.81 C9.08,7.81 1.66,0.25 1.66,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="50" + android:startOffset="100" + android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 9.08,7.81 9.08,7.81 C9.08,7.81 1.66,0.25 1.66,0.25c " + android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.95,9.61 7.95,9.61 C7.95,9.61 9.58,8.01 9.58,8.01 C9.58,8.01 1.66,0.25 1.66,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateXY" android:duration="250" + android:startOffset="0" android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 0,3.439C 0,2.24316667461395 0,-2.54016667461395 0,-3.736"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="rotation" android:duration="200" + android:startOffset="0" android:valueFrom="0" android:valueTo="-90" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="0" + android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.24,9.63 0.24,9.63 C0.24,9.63 -0.15,7.2 -0.15,7.2 C-0.15,7.2 6.84,0.25 6.84,0.25c " + android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.22,9.59 0.22,9.59 C0.22,9.59 -0.4,7.45 -0.4,7.45 C-0.4,7.45 6.84,0.25 6.84,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="17" + android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.22,9.59 0.22,9.59 C0.22,9.59 -0.4,7.45 -0.4,7.45 C-0.4,7.45 6.84,0.25 6.84,0.25c " + android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.36,9.46 0.36,9.46 C0.36,9.46 -0.44,7.6 -0.44,7.6 C-0.44,7.6 6.84,0.25 6.84,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="33" + android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.36,9.46 0.36,9.46 C0.36,9.46 -0.44,7.6 -0.44,7.6 C-0.44,7.6 6.84,0.25 6.84,0.25c " + android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.71,9.4 0.71,9.4 C0.71,9.4 -0.61,7.63 -0.61,7.63 C-0.61,7.63 6.84,0.25 6.84,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="33" + android:startOffset="50" + android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.71,9.4 0.71,9.4 C0.71,9.4 -0.61,7.63 -0.61,7.63 C-0.61,7.63 6.84,0.25 6.84,0.25c " + android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.93,9.29 0.93,9.29 C0.93,9.29 -0.5,7.69 -0.5,7.69 C-0.5,7.69 6.84,0.25 6.84,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="83" + android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.93,9.29 0.93,9.29 C0.93,9.29 -0.5,7.69 -0.5,7.69 C-0.5,7.69 6.84,0.25 6.84,0.25c " + android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.84,9.33 0.84,9.33 C0.84,9.33 -0.53,7.8 -0.53,7.8 C-0.53,7.8 6.84,0.25 6.84,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="117" + android:startOffset="100" + android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.84,9.33 0.84,9.33 C0.84,9.33 -0.53,7.8 -0.53,7.8 C-0.53,7.8 6.84,0.25 6.84,0.25c " + android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.56,9.63 0.56,9.63 C0.56,9.63 -1.08,8.07 -1.08,8.07 C-1.08,8.07 6.84,0.25 6.84,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateXY" android:duration="250" + android:startOffset="0" android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 0,3.439C 0,2.24316667461395 0,-2.54016667461395 0,-3.736"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="rotation" android:duration="200" + android:startOffset="0" android:valueFrom="0" android:valueTo="90" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateX" android:duration="267" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> + </set> + </aapt:attr> + </target> </animated-vector> diff --git a/packages/SystemUI/res/layout-land/global_actions_column.xml b/packages/SystemUI/res/layout-land/global_actions_column.xml new file mode 100644 index 000000000000..99a4e13d2064 --- /dev/null +++ b/packages/SystemUI/res/layout-land/global_actions_column.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> + +<com.android.systemui.globalactions.GlobalActionsColumnLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@id/global_actions_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal" + android:clipToPadding="false" + android:theme="@style/qs_theme" + android:gravity="center_horizontal | top" + android:clipChildren="false" +> + <LinearLayout + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:padding="0dp" + android:orientation="horizontal" + android:clipChildren="false" + android:clipToPadding="false" + > + <!-- Grid of action items --> + <LinearLayout + android:id="@android:id/list" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="@dimen/global_actions_grid_side_margin" + android:translationZ="@dimen/global_actions_translate" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:background="?android:attr/colorBackgroundFloating" + /> + <!-- For separated items--> + <LinearLayout + android:id="@+id/separated_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/global_actions_grid_side_margin" + android:layout_marginTop="@dimen/global_actions_grid_side_margin" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:orientation="horizontal" + android:background="?android:attr/colorBackgroundFloating" + android:translationZ="@dimen/global_actions_translate" + /> + </LinearLayout> + +</com.android.systemui.globalactions.GlobalActionsColumnLayout> diff --git a/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml b/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml new file mode 100644 index 000000000000..0f8613194959 --- /dev/null +++ b/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> + +<com.android.systemui.globalactions.GlobalActionsColumnLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@id/global_actions_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal" + android:clipToPadding="false" + android:theme="@style/qs_theme" + android:gravity="center_horizontal | bottom" + android:clipChildren="false" +> + <LinearLayout + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:padding="0dp" + android:orientation="horizontal" + > + <!-- Grid of action items --> + <com.android.systemui.globalactions.ListGridLayout + android:id="@android:id/list" + android:layout_gravity="bottom|left" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginBottom="@dimen/global_actions_grid_side_margin" + android:translationZ="@dimen/global_actions_translate" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:background="?android:attr/colorBackgroundFloating" + /> + <!-- For separated items--> + <LinearLayout + android:id="@+id/separated_button" + android:layout_gravity="top|left" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginRight="@dimen/global_actions_grid_side_margin" + android:layout_marginBottom="@dimen/global_actions_grid_side_margin" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:orientation="horizontal" + android:background="?android:attr/colorBackgroundFloating" + android:translationZ="@dimen/global_actions_translate" + /> + </LinearLayout> + +</com.android.systemui.globalactions.GlobalActionsColumnLayout> diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml index 65ede3def821..b73412526f5e 100644 --- a/packages/SystemUI/res/layout/bubble_expanded_view.xml +++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml @@ -33,13 +33,15 @@ android:layout_height="wrap_content" android:animateLayoutChanges="true"> - <ImageView + <com.android.systemui.statusbar.AlphaOptimizedButton + style="@android:style/Widget.Material.Button.Borderless" android:id="@+id/settings_button" - android:layout_width="@dimen/bubble_header_icon_size" - android:layout_height="@dimen/bubble_header_icon_size" - android:src="@drawable/ic_settings" - android:scaleType="center" - android:layout_gravity="end"/> + android:layout_gravity="end" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:focusable="true" + android:text="@string/manage_bubbles_text" + android:textColor="?attr/wallpaperTextColor"/> <include layout="@layout/bubble_permission_view" android:id="@+id/permission_layout" diff --git a/packages/SystemUI/res/layout/global_actions_column.xml b/packages/SystemUI/res/layout/global_actions_column.xml new file mode 100644 index 000000000000..b58146b6ce41 --- /dev/null +++ b/packages/SystemUI/res/layout/global_actions_column.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> + +<com.android.systemui.globalactions.GlobalActionsColumnLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@id/global_actions_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:clipToPadding="false" + android:theme="@style/qs_theme" + android:gravity="center_vertical | right" + android:clipChildren="false" +> + <LinearLayout + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:gravity="top | right" + android:orientation="vertical" + android:padding="0dp" + android:layout_marginTop="@dimen/global_actions_grid_container_bottom_margin" + android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin" + > + <!-- Global actions is right-aligned to be physically near power button --> + <LinearLayout + android:id="@android:id/list" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:gravity="right" + android:translationZ="@dimen/global_actions_translate" + android:layout_marginRight="@dimen/global_actions_grid_side_margin" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + /> + + <!-- For separated items--> + <LinearLayout + android:id="@+id/separated_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/global_actions_grid_side_margin" + android:layout_marginRight="@dimen/global_actions_grid_side_margin" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:orientation="vertical" + android:gravity="center" + android:translationZ="@dimen/global_actions_translate" + /> + </LinearLayout> + +</com.android.systemui.globalactions.GlobalActionsColumnLayout> diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index f9bf47bb7d08..89d1a19737ee 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -37,16 +37,17 @@ <color name="notification_ripple_untinted_color">#30ffffff</color> <!-- The "inside" of a notification, reached via longpress --> - <color name="notification_guts_bg_color">@*android:color/notification_material_background_color</color> + <color name="notification_guts_bg_color">@color/GM2_grey_900</color> <!-- The color of the text inside a notification --> <color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color> - <color name="notification_guts_selection_bg">#202124</color> - <color name="notification_guts_selection_border">#669DF6</color> - <color name="notification_guts_link_icon_tint">@color/GM2_grey_200</color> + <color name="notification_guts_selection_bg">@color/GM2_grey_800</color> + <color name="notification_guts_selection_border">@color/GM2_grey_700</color> + <color name="notification_guts_link_icon_tint">@color/GM2_grey_500</color> <color name="notification_guts_sub_text_color">@color/GM2_grey_200</color> - <color name="notification_guts_header_text_color">@color/GM2_grey_100</color> + <color name="notification_guts_header_text_color">@color/GM2_grey_200</color> + <color name="notification_guts_button_color">@color/GM2_blue_200</color> <!-- The color of the background in the top part of QSCustomizer --> <color name="qs_customize_background">@color/GM2_grey_900</color> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 4e1a7d07e687..b673e5207f80 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -95,6 +95,7 @@ <color name="notification_guts_header_text_color">@color/GM2_grey_900</color> <color name="notification_silence_color">#FF32c1de</color> <color name="notification_alert_color">#FFF87B2B</color> + <color name="notification_guts_button_color">@color/GM2_blue_700</color> <color name="assist_orb_color">#ffffff</color> @@ -177,6 +178,9 @@ <color name="GM2_red_300">#F28B82</color> <color name="GM2_red_500">#B71C1C</color> + <color name="GM2_blue_200">#AECBFA</color> + <color name="GM2_blue_700">#1967D2</color> + <color name="GM2_yellow_500">#FFFBBC04</color> <color name="GM2_green_500">#FF34A853</color> </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 4abe9f0bfb5c..2871d06d8f8e 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -494,4 +494,7 @@ <!-- Launcher package name for overlaying icons. --> <string name="launcher_overlayable_package" translatable="false">com.android.launcher3</string> + <!-- ThemePicker package name for overlaying icons. --> + <string name="themepicker_overlayable_package" translatable="false">com.android.wallpaper</string> + </resources> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 501b1b56a848..f75f255324a6 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -108,6 +108,8 @@ <item type="id" name="display_cutout" /> + <item type="id" name="row_tag_for_content_view" /> + <!-- Optional cancel button on Keyguard --> <item type="id" name="cancel_button"/> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index cea336c82327..dc35653e5f7d 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2414,6 +2414,8 @@ <!-- Text for asking the user whether bubbles (floating app content) should be enabled for an app. [CHAR LIMIT=NONE] --> <string name="bubbles_prompt">Allow bubbles from <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string> + <!-- The text for the manage bubbles link. [CHAR LIMIT=NONE] --> + <string name="manage_bubbles_text">Manage</string> <!-- Text used for button allowing user to opt out of bubbles [CHAR LIMIT=20] --> <string name="no_bubbles">Deny</string> <!-- Text used for button allowing user to approve / enable bubbles [CHAR LIMIT=20] --> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 23de2acf14c9..aa89dce4114a 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -452,7 +452,7 @@ <style name="TextAppearance.NotificationInfo.Button"> <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item> <item name="android:textSize">16sp</item> - <item name="android:textColor">?android:attr/colorAccent</item> + <item name="android:textColor">@color/notification_guts_button_color</item> <item name="android:background">@drawable/btn_borderless_rect</item> <item name="android:gravity">center_vertical</item> <item name="android:focusable">true</item> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java index 40d98c1c08e4..f384507d302c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java @@ -151,19 +151,34 @@ public class PluginInstanceManager<T extends Plugin> { return plugins.size() != 0; } - private void disable(PluginInfo info, - @PluginEnabler.DisableReason int reason) { + private boolean isPluginWhitelisted(ComponentName pluginName) { + for (String componentNameOrPackage : mWhitelistedPlugins) { + ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage); + if (componentName == null) { + if (componentNameOrPackage.equals(pluginName.getPackageName())) { + return true; + } + } else { + if (componentName.equals(pluginName)) { + return true; + } + } + } + return false; + } + + private void disable(PluginInfo info, @PluginEnabler.DisableReason int reason) { // Live by the sword, die by the sword. // Misbehaving plugins get disabled and won't come back until uninstall/reinstall. + ComponentName pluginComponent = new ComponentName(info.mPackage, info.mClass); // If a plugin is detected in the stack of a crash then this will be called for that // plugin, if the plugin causing a crash cannot be identified, they are all disabled // assuming one of them must be bad. - if (mWhitelistedPlugins.contains(info.mPackage)) { + if (isPluginWhitelisted(pluginComponent)) { // Don't disable whitelisted plugins as they are a part of the OS. return; } - ComponentName pluginComponent = new ComponentName(info.mPackage, info.mClass); Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString()); mManager.getPluginEnabler().setDisabled(pluginComponent, reason); } @@ -288,6 +303,13 @@ public class PluginInstanceManager<T extends Plugin> { if (result.size() > 1 && !mAllowMultiple) { // TODO: Show warning. Log.w(TAG, "Multiple plugins found for " + mAction); + if (DEBUG) { + for (ResolveInfo info : result) { + ComponentName name = new ComponentName(info.serviceInfo.packageName, + info.serviceInfo.name); + Log.w(TAG, " " + name); + } + } return; } for (ResolveInfo info : result) { @@ -305,7 +327,7 @@ public class PluginInstanceManager<T extends Plugin> { protected PluginInfo<T> handleLoadPlugin(ComponentName component) { // This was already checked, but do it again here to make extra extra sure, we don't // use these on production builds. - if (!isDebuggable && !mWhitelistedPlugins.contains(component.getPackageName())) { + if (!isDebuggable && !isPluginWhitelisted(component)) { // Never ever ever allow these on production builds, they are only for prototyping. Log.w(TAG, "Plugin cannot be loaded on production build: " + component); return null; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java index 6e3eb7c397bc..d250acca32c5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java @@ -70,6 +70,15 @@ public class TaskStackChangeListeners extends TaskStackListener { public void removeListener(TaskStackChangeListener listener) { mTaskStackListeners.remove(listener); + if (mTaskStackListeners.isEmpty() && mRegistered) { + // Unregister mTaskStackListener once we have no more listeners + try { + ActivityTaskManager.getService().unregisterTaskStackListener(this); + mRegistered = false; + } catch (Exception e) { + Log.w(TAG, "Failed to call unregisterTaskStackListener", e); + } + } } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index 07c2f10e58b3..9f4c403ed787 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -32,7 +32,6 @@ import android.view.LayoutInflater; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.Observer; -import com.android.systemui.SysUiServiceProvider; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManager.DockEventListener; @@ -89,6 +88,7 @@ public final class ClockManager { private final Observer<Integer> mCurrentUserObserver = (newUserId) -> reload(); private final PluginManager mPluginManager; + @Nullable private final DockManager mDockManager; /** * Observe changes to dock state to know when to switch the clock face. @@ -102,7 +102,6 @@ public final class ClockManager { reload(); } }; - @Nullable private DockManager mDockManager; /** * When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face @@ -125,21 +124,24 @@ public final class ClockManager { @Inject public ClockManager(Context context, InjectionInflationController injectionInflater, - PluginManager pluginManager, SysuiColorExtractor colorExtractor) { + PluginManager pluginManager, SysuiColorExtractor colorExtractor, + @Nullable DockManager dockManager) { this(context, injectionInflater, pluginManager, colorExtractor, context.getContentResolver(), new CurrentUserObservable(context), - new SettingsWrapper(context.getContentResolver())); + new SettingsWrapper(context.getContentResolver()), dockManager); } + @VisibleForTesting ClockManager(Context context, InjectionInflationController injectionInflater, PluginManager pluginManager, SysuiColorExtractor colorExtractor, ContentResolver contentResolver, CurrentUserObservable currentUserObservable, - SettingsWrapper settingsWrapper) { + SettingsWrapper settingsWrapper, DockManager dockManager) { mContext = context; mPluginManager = pluginManager; mContentResolver = contentResolver; mSettingsWrapper = settingsWrapper; mCurrentUserObservable = currentUserObservable; + mDockManager = dockManager; mPreviewClocks = new AvailableClocks(); Resources res = context.getResources(); @@ -223,9 +225,6 @@ public final class ClockManager { Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE), false, mContentObserver, UserHandle.USER_ALL); mCurrentUserObservable.getCurrentUser().observeForever(mCurrentUserObserver); - if (mDockManager == null) { - mDockManager = SysUiServiceProvider.getComponent(mContext, DockManager.class); - } if (mDockManager != null) { mDockManager.addListener(mDockEventListener); } diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java index 04f887bb6b2f..41a7bc43eaa0 100644 --- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java @@ -16,6 +16,7 @@ package com.android.systemui; import android.app.PendingIntent; import android.content.Intent; +import android.view.View; import com.android.systemui.plugins.ActivityStarter; @@ -53,6 +54,16 @@ public class ActivityStarterDelegate implements ActivityStarter { } @Override + public void startPendingIntentDismissingKeyguard(PendingIntent intent, + Runnable intentSentCallback, View associatedView) { + if (mActualStarter == null) { + return; + } + mActualStarter.startPendingIntentDismissingKeyguard(intent, intentSentCallback, + associatedView); + } + + @Override public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags) { if (mActualStarter == null) { diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java index 802903db7ba8..ad2e0024065f 100644 --- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java +++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java @@ -80,16 +80,6 @@ public class HardwareUiLayout extends MultiListLayout implements Tunable { } @Override - public void removeAllItems() { - if (mList != null) { - mList.removeAllViews(); - } - if (mSeparatedView != null) { - mSeparatedView.removeAllViews(); - } - } - - @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); updateSettings(); diff --git a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java index f8287a4dc2dc..d153fb039802 100644 --- a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java +++ b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java @@ -45,11 +45,6 @@ public abstract class MultiListLayout extends LinearLayout { protected abstract ViewGroup getListView(); /** - * Removes all child items from the separated and list views, if they exist. - */ - protected abstract void removeAllItems(); - - /** * Sets the divided view, which may have a differently-colored background. */ public abstract void setDivisionView(View v); @@ -110,6 +105,25 @@ public abstract class MultiListLayout extends LinearLayout { onUpdateList(); } + protected void removeAllSeparatedViews() { + ViewGroup separated = getSeparatedView(); + if (separated != null) { + separated.removeAllViews(); + } + } + + protected void removeAllListViews() { + ViewGroup list = getListView(); + if (list != null) { + list.removeAllViews(); + } + } + + protected void removeAllItems() { + removeAllListViews(); + removeAllSeparatedViews(); + } + protected void onUpdateList() { removeAllItems(); setSeparatedViewVisibility(mAdapter.hasSeparatedItems()); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 346660df7fb5..67f327490a6e 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -40,11 +40,8 @@ import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Insets; import android.graphics.Point; -import android.graphics.drawable.AdaptiveIconDrawable; -import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.InsetDrawable; import android.graphics.drawable.ShapeDrawable; import android.os.RemoteException; import android.os.ServiceManager; @@ -66,6 +63,7 @@ import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.recents.TriangleShape; +import com.android.systemui.statusbar.AlphaOptimizedButton; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.ExpandableViewState; @@ -80,7 +78,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList private View mPointerView; private int mPointerMargin; - private ImageView mSettingsIcon; + private AlphaOptimizedButton mSettingsIcon; // Permission view private View mPermissionView; @@ -100,8 +98,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList private int mSettingsIconHeight; private int mBubbleHeight; private int mPermissionHeight; - private int mIconInset; - private Drawable mSettingsIconDrawable; private int mPointerWidth; private int mPointerHeight; @@ -218,10 +214,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList mSettingsIconHeight = getContext().getResources().getDimensionPixelSize( R.dimen.bubble_expanded_header_height); mSettingsIcon = findViewById(R.id.settings_button); - mIconInset = getResources().getDimensionPixelSize(R.dimen.bubble_icon_inset); mSettingsIcon.setOnClickListener(this); - // Save initial drawable to create adaptive icons that will take its place. - mSettingsIconDrawable = mSettingsIcon.getDrawable(); mPermissionHeight = getContext().getResources().getDimensionPixelSize( R.dimen.bubble_permission_height); @@ -377,16 +370,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList int foregroundColor = ta.getColor(1, Color.BLACK /* default */); ta.recycle(); - // Must clear tint first - otherwise tint updates inconsistently. - mSettingsIconDrawable.setTintList(null); - mSettingsIconDrawable.setTint(foregroundColor); - - InsetDrawable foreground = new InsetDrawable(mSettingsIconDrawable, mIconInset); - ColorDrawable background = new ColorDrawable(backgroundColor); - AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(background, - foreground); - mSettingsIcon.setImageDrawable(adaptiveIcon); - // Update permission prompt color. mPermissionView.setBackground(createPermissionBackground(backgroundColor)); mPermissionPrompt.setTextColor(foregroundColor); @@ -649,11 +632,12 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList } private Intent getSettingsIntent(String packageName, final int appUid) { - final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS); + final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS); intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName); intent.putExtra(Settings.EXTRA_APP_UID, appUid); intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); return intent; } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java index a63fdbd63e02..d4e7bbca1107 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java @@ -32,6 +32,7 @@ import android.view.InputDevice; import android.view.MotionEvent; import android.view.accessibility.AccessibilityManager; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.Dependency; import com.android.systemui.UiOffloadThread; import com.android.systemui.analytics.DataCollector; @@ -62,6 +63,8 @@ public class FalsingManager implements SensorEventListener, StateListener { Sensor.TYPE_LIGHT, Sensor.TYPE_ROTATION_VECTOR, }; + private static final String FALSING_REMAIN_LOCKED = "falsing_failure_after_attempts"; + private static final String FALSING_SUCCESS = "falsing_success_after_attempts"; private final Handler mHandler = new Handler(Looper.getMainLooper()); private final Context mContext; @@ -83,6 +86,8 @@ public class FalsingManager implements SensorEventListener, StateListener { private boolean mScreenOn; private boolean mShowingAod; private Runnable mPendingWtf; + private int mIsFalseTouchCalls; + private MetricsLogger mMetricsLogger; protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { @Override @@ -99,6 +104,7 @@ public class FalsingManager implements SensorEventListener, StateListener { mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext); mUiOffloadThread = Dependency.get(UiOffloadThread.class); mScreenOn = context.getSystemService(PowerManager.class).isInteractive(); + mMetricsLogger = new MetricsLogger(); mContext.getContentResolver().registerContentObserver( Settings.Secure.getUriFor(ENFORCE_BOUNCER), false, @@ -143,6 +149,14 @@ public class FalsingManager implements SensorEventListener, StateListener { private void sessionExitpoint(boolean force) { if (mSessionActive && (force || !shouldSessionBeActive())) { mSessionActive = false; + if (mIsFalseTouchCalls != 0) { + if (FalsingLog.ENABLED) { + FalsingLog.i( + "isFalseTouchCalls", "Calls before failure: " + mIsFalseTouchCalls); + } + mMetricsLogger.histogram(FALSING_REMAIN_LOCKED, mIsFalseTouchCalls); + mIsFalseTouchCalls = 0; + } // This can be expensive, and doesn't need to happen on the main thread. mUiOffloadThread.submit(() -> { @@ -166,6 +180,7 @@ public class FalsingManager implements SensorEventListener, StateListener { } mBouncerOn = false; mSessionActive = true; + mIsFalseTouchCalls = 0; if (mHumanInteractionClassifier.isEnabled()) { registerSensors(CLASSIFIER_SENSORS); @@ -250,7 +265,16 @@ public class FalsingManager implements SensorEventListener, StateListener { // anti-falsed. return false; } - return mHumanInteractionClassifier.isFalseTouch(); + mIsFalseTouchCalls++; + boolean isFalse = mHumanInteractionClassifier.isFalseTouch(); + if (!isFalse) { + if (FalsingLog.ENABLED) { + FalsingLog.i("isFalseTouchCalls", "Calls before success: " + mIsFalseTouchCalls); + } + mMetricsLogger.histogram(FALSING_SUCCESS, mIsFalseTouchCalls); + mIsFalseTouchCalls = 0; + } + return isFalse; } private void clearPendingWtf() { diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java new file mode 100644 index 000000000000..59070287cc5c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.globalactions; + +import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE; +import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE; +import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.R; + +/** + * Grid-based implementation of the button layout created by the global actions dialog. + */ +public class GlobalActionsColumnLayout extends GlobalActionsLayout { + private boolean mLastSnap; + + public GlobalActionsColumnLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + + post(() -> updateSnap()); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @VisibleForTesting + protected boolean shouldReverseListItems() { + int rotation = getCurrentRotation(); + if (rotation == ROTATION_NONE) { + return false; + } + if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + return rotation == ROTATION_LANDSCAPE; + } + return rotation == ROTATION_SEASCAPE; + } + + @Override + public void onUpdateList() { + super.onUpdateList(); + updateChildOrdering(); + } + + private void updateChildOrdering() { + if (shouldReverseListItems()) { + getListView().bringToFront(); + } else { + getSeparatedView().bringToFront(); + } + } + + /** + * Snap this layout to align with the power button. + */ + @VisibleForTesting + protected void snapToPowerButton() { + int offset = getPowerButtonOffsetDistance(); + switch (getCurrentRotation()) { + case (ROTATION_LANDSCAPE): + setPadding(offset, 0, 0, 0); + setGravity(Gravity.LEFT | Gravity.TOP); + break; + case (ROTATION_SEASCAPE): + setPadding(0, 0, offset, 0); + setGravity(Gravity.RIGHT | Gravity.BOTTOM); + break; + default: + setPadding(0, offset, 0, 0); + setGravity(Gravity.TOP | Gravity.RIGHT); + break; + } + } + + /** + * Detach this layout from snapping to the power button and instead center along that edge. + */ + @VisibleForTesting + protected void centerAlongEdge() { + switch (getCurrentRotation()) { + case (ROTATION_LANDSCAPE): + setPadding(0, 0, 0, 0); + setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP); + break; + case (ROTATION_SEASCAPE): + setPadding(0, 0, 0, 0); + setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM); + break; + default: + setPadding(0, 0, 0, 0); + setGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT); + break; + } + } + + /** + * Determines the distance from the top of the screen to the power button. + */ + @VisibleForTesting + protected int getPowerButtonOffsetDistance() { + return Math.round(getContext().getResources().getDimension( + R.dimen.global_actions_top_padding)); + } + + /** + * Check whether there is enough extra space below the dialog such that we can offset the top + * of the dialog from the top of the phone to line it up with the power button, then either + * snap the dialog to the power button or center it along the edge with snapToPowerButton. + */ + @VisibleForTesting + protected boolean shouldSnapToPowerButton() { + int offsetSize = getPowerButtonOffsetDistance(); + int dialogSize; + int screenSize; + View wrapper = getWrapper(); + int rotation = getCurrentRotation(); + if (rotation == ROTATION_NONE) { + dialogSize = wrapper.getMeasuredHeight(); + screenSize = getMeasuredHeight(); + } else { + dialogSize = wrapper.getMeasuredWidth(); + screenSize = getMeasuredWidth(); + } + return dialogSize + offsetSize < screenSize; + } + + @VisibleForTesting + protected void updateSnap() { + boolean snap = shouldSnapToPowerButton(); + if (snap != mLastSnap) { + if (snap) { + snapToPowerButton(); + } else { + centerAlongEdge(); + } + } + mLastSnap = snap; + } + + @VisibleForTesting + protected float getGridItemSize() { + return getContext().getResources().getDimension(R.dimen.global_actions_grid_item_height); + } + + @VisibleForTesting + protected float getAnimationDistance() { + return getGridItemSize() / 2; + } + + @Override + public float getAnimationOffsetX() { + if (getCurrentRotation() == ROTATION_NONE) { + return getAnimationDistance(); + } + return 0; + } + + @Override + public float getAnimationOffsetY() { + switch (getCurrentRotation()) { + case ROTATION_LANDSCAPE: + return -getAnimationDistance(); + case ROTATION_SEASCAPE: + return getAnimationDistance(); + default: // Portrait + return 0; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 64511a0b4cc3..d6c897186628 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -410,8 +410,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mActivityStarter .startPendingIntentDismissingKeyguard(intent); } - }) + }, + mKeyguardManager.isDeviceLocked()) : null; + ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController); dialog.setCanceledOnTouchOutside(false); // Handled by the custom class. dialog.setKeyguardShowing(mKeyguardShowing); @@ -1583,13 +1585,20 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } private int getGlobalActionsLayoutId(Context context) { - if (isForceGridEnabled(context) || shouldUsePanel()) { - if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) { + boolean useGridLayout = isForceGridEnabled(context) || shouldUsePanel(); + if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) { + if (useGridLayout) { return com.android.systemui.R.layout.global_actions_grid_seascape; + } else { + return com.android.systemui.R.layout.global_actions_column_seascape; + } + } else { + if (useGridLayout) { + return com.android.systemui.R.layout.global_actions_grid; + } else { + return com.android.systemui.R.layout.global_actions_column; } - return com.android.systemui.R.layout.global_actions_grid; } - return com.android.systemui.R.layout.global_actions_wrapped; } @Override @@ -1712,7 +1721,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } public void onRotate(int from, int to) { - if (mShowing && (shouldUsePanel() || isForceGridEnabled(mContext))) { + if (mShowing) { refreshDialog(); } } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java index 554ed738c335..e1462d15c887 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java @@ -22,58 +22,23 @@ import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE; import android.content.Context; import android.util.AttributeSet; -import android.util.Log; import android.view.View; import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.HardwareBgDrawable; -import com.android.systemui.MultiListLayout; -import com.android.systemui.util.leak.RotationUtils; /** * Grid-based implementation of the button layout created by the global actions dialog. */ -public class GlobalActionsGridLayout extends MultiListLayout { - - boolean mBackgroundsSet; - +public class GlobalActionsGridLayout extends GlobalActionsLayout { public GlobalActionsGridLayout(Context context, AttributeSet attrs) { super(context, attrs); } - private void setBackgrounds() { - int gridBackgroundColor = getResources().getColor( - com.android.systemui.R.color.global_actions_grid_background, null); - int separatedBackgroundColor = getResources().getColor( - com.android.systemui.R.color.global_actions_separated_background, null); - HardwareBgDrawable listBackground = new HardwareBgDrawable(true, true, getContext()); - HardwareBgDrawable separatedBackground = new HardwareBgDrawable(true, true, getContext()); - listBackground.setTint(gridBackgroundColor); - separatedBackground.setTint(separatedBackgroundColor); - getListView().setBackground(listBackground); - getSeparatedView().setBackground(separatedBackground); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - // backgrounds set only once, the first time onMeasure is called after inflation - if (getListView() != null && !mBackgroundsSet) { - setBackgrounds(); - mBackgroundsSet = true; - } - } - - @VisibleForTesting - protected int getCurrentRotation() { - return RotationUtils.getRotation(mContext); - } - @VisibleForTesting - protected void setupListView(ListGridLayout listView, int itemCount) { - listView.setExpectedCount(itemCount); + protected void setupListView() { + ListGridLayout listView = getListView(); + listView.setExpectedCount(mAdapter.countListItems()); listView.setReverseSublists(shouldReverseSublists()); listView.setReverseItems(shouldReverseListItems()); listView.setSwapRowsAndColumns(shouldSwapRowsAndColumns()); @@ -81,29 +46,8 @@ public class GlobalActionsGridLayout extends MultiListLayout { @Override public void onUpdateList() { + setupListView(); super.onUpdateList(); - - ViewGroup separatedView = getSeparatedView(); - ListGridLayout listView = getListView(); - setupListView(listView, mAdapter.countListItems()); - - for (int i = 0; i < mAdapter.getCount(); i++) { - // generate the view item - View v; - boolean separated = mAdapter.shouldBeSeparated(i); - if (separated) { - v = mAdapter.getView(i, null, separatedView); - } else { - v = mAdapter.getView(i, null, listView); - } - Log.d("GlobalActionsGridLayout", "View: " + v); - - if (separated) { - separatedView.addView(v); - } else { - listView.addItem(v); - } - } updateSeparatedItemSize(); } @@ -111,7 +55,8 @@ public class GlobalActionsGridLayout extends MultiListLayout { * If the separated view contains only one item, expand the bounds of that item to take up the * entire view, so that the whole thing is touch-able. */ - private void updateSeparatedItemSize() { + @VisibleForTesting + protected void updateSeparatedItemSize() { ViewGroup separated = getSeparatedView(); if (separated.getChildCount() == 0) { return; @@ -129,13 +74,24 @@ public class GlobalActionsGridLayout extends MultiListLayout { } @Override - protected ViewGroup getSeparatedView() { - return findViewById(com.android.systemui.R.id.separated_button); + protected ListGridLayout getListView() { + return (ListGridLayout) super.getListView(); } @Override - protected ListGridLayout getListView() { - return findViewById(android.R.id.list); + protected void removeAllListViews() { + ListGridLayout list = getListView(); + if (list != null) { + list.removeAllItems(); + } + } + + @Override + protected void addToListView(View v, boolean reverse) { + ListGridLayout list = getListView(); + if (list != null) { + list.addItem(v); + } } @Override @@ -174,12 +130,7 @@ public class GlobalActionsGridLayout extends MultiListLayout { return true; } - /** - * Determines whether the ListGridLayout should reverse the ordering of items within sublists. - * Used for RTL languages to ensure that items appear in the same positions, without having to - * override layoutDirection, which breaks Talkback ordering. - */ - @VisibleForTesting + @Override protected boolean shouldReverseListItems() { int rotation = getCurrentRotation(); boolean reverse = false; // should we add items to parents in the reverse order? @@ -187,20 +138,13 @@ public class GlobalActionsGridLayout extends MultiListLayout { || rotation == ROTATION_SEASCAPE) { reverse = !reverse; // if we're in portrait or seascape, reverse items } - if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { reverse = !reverse; // if we're in an RTL language, reverse items (again) } return reverse; } - /** - * Not ued in this implementation of the Global Actions Menu, but necessary for some others. - */ - @Override - public void setDivisionView(View v) { - // do nothing - } - + @VisibleForTesting protected float getAnimationDistance() { int rows = getListView().getRowCount(); float gridItemSize = getContext().getResources().getDimension( diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java new file mode 100644 index 000000000000..f755a93ed275 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.globalactions; + +import android.content.Context; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.HardwareBgDrawable; +import com.android.systemui.MultiListLayout; +import com.android.systemui.R; +import com.android.systemui.util.leak.RotationUtils; + +import java.util.Locale; + +/** + * Grid-based implementation of the button layout created by the global actions dialog. + */ +public abstract class GlobalActionsLayout extends MultiListLayout { + + boolean mBackgroundsSet; + + public GlobalActionsLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + private void setBackgrounds() { + int gridBackgroundColor = getResources().getColor( + R.color.global_actions_grid_background, null); + int separatedBackgroundColor = getResources().getColor( + R.color.global_actions_separated_background, null); + HardwareBgDrawable listBackground = new HardwareBgDrawable(true, true, getContext()); + HardwareBgDrawable separatedBackground = new HardwareBgDrawable(true, true, getContext()); + listBackground.setTint(gridBackgroundColor); + separatedBackground.setTint(separatedBackgroundColor); + getListView().setBackground(listBackground); + getSeparatedView().setBackground(separatedBackground); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + // backgrounds set only once, the first time onMeasure is called after inflation + if (getListView() != null && !mBackgroundsSet) { + setBackgrounds(); + mBackgroundsSet = true; + } + } + + protected void addToListView(View v, boolean reverse) { + if (reverse) { + getListView().addView(v, 0); + } else { + getListView().addView(v); + } + } + + protected void addToSeparatedView(View v, boolean reverse) { + if (reverse) { + getSeparatedView().addView(v, 0); + } else { + getSeparatedView().addView(v); + } + } + + @VisibleForTesting + protected int getCurrentLayoutDirection() { + return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()); + } + + @VisibleForTesting + protected int getCurrentRotation() { + return RotationUtils.getRotation(mContext); + } + + /** + * Determines whether the ListGridLayout should reverse the ordering of items within sublists. + * Used for RTL languages to ensure that items appear in the same positions, without having to + * override layoutDirection, which breaks Talkback ordering. + */ + protected abstract boolean shouldReverseListItems(); + + @Override + public void onUpdateList() { + super.onUpdateList(); + + ViewGroup separatedView = getSeparatedView(); + ViewGroup listView = getListView(); + + for (int i = 0; i < mAdapter.getCount(); i++) { + // generate the view item + View v; + boolean separated = mAdapter.shouldBeSeparated(i); + if (separated) { + v = mAdapter.getView(i, null, separatedView); + } else { + v = mAdapter.getView(i, null, listView); + } + if (separated) { + addToSeparatedView(v, false); + } else { + addToListView(v, shouldReverseListItems()); + } + } + } + + @Override + protected ViewGroup getSeparatedView() { + return findViewById(R.id.separated_button); + } + + @Override + protected ViewGroup getListView() { + return findViewById(android.R.id.list); + } + + protected View getWrapper() { + return getChildAt(0); + } + + /** + * Not used in this implementation of the Global Actions Menu, but necessary for some others. + */ + @Override + public void setDivisionView(View v) { + // do nothing + } +} diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java index 477e7d7ebf72..24a4b9e3052b 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java @@ -30,13 +30,15 @@ import android.os.Message; import android.util.Log; /** - * A helper class that computes histogram and percentile 85 from a bitmap. - * Percentile 85 will be computed each time the user picks a new image wallpaper. + * A helper class that computes threshold from a bitmap. + * Threshold will be computed each time the user picks a new image wallpaper. */ class ImageProcessHelper { private static final String TAG = ImageProcessHelper.class.getSimpleName(); - private static final float DEFAULT_PER85 = 0.8f; - private static final int MSG_UPDATE_PER85 = 1; + private static final float DEFAULT_THRESHOLD = 0.8f; + private static final float DEFAULT_OTSU_THRESHOLD = 0f; + private static final float MAX_THRESHOLD = 0.89f; + private static final int MSG_UPDATE_THRESHOLD = 1; /** * This color matrix will be applied to each pixel to get luminance from rgb by below formula: @@ -53,8 +55,8 @@ class ImageProcessHelper { @Override public boolean handleMessage(Message msg) { switch (msg.what) { - case MSG_UPDATE_PER85: - mPer85 = (float) msg.obj; + case MSG_UPDATE_THRESHOLD: + mThreshold = (float) msg.obj; return true; default: return false; @@ -62,20 +64,20 @@ class ImageProcessHelper { } }); - private float mPer85 = DEFAULT_PER85; + private float mThreshold = DEFAULT_THRESHOLD; - void startComputingPercentile85(Bitmap bitmap) { - new Per85ComputeTask(mHandler).execute(bitmap); + void start(Bitmap bitmap) { + new ThresholdComputeTask(mHandler).execute(bitmap); } - float getPercentile85() { - return mPer85; + float getThreshold() { + return Math.min(mThreshold, MAX_THRESHOLD); } - private static class Per85ComputeTask extends AsyncTask<Bitmap, Void, Float> { + private static class ThresholdComputeTask extends AsyncTask<Bitmap, Void, Float> { private Handler mUpdateHandler; - Per85ComputeTask(Handler handler) { + ThresholdComputeTask(Handler handler) { super(handler); mUpdateHandler = handler; } @@ -84,35 +86,55 @@ class ImageProcessHelper { protected Float doInBackground(Bitmap... bitmaps) { Bitmap bitmap = bitmaps[0]; if (bitmap != null) { - int[] histogram = processHistogram(bitmap); - return computePercentile85(bitmap, histogram); + return new Threshold().compute(bitmap); } - Log.e(TAG, "Per85ComputeTask: Can't get bitmap"); - return DEFAULT_PER85; + Log.e(TAG, "ThresholdComputeTask: Can't get bitmap"); + return DEFAULT_THRESHOLD; } @Override protected void onPostExecute(Float result) { - Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_PER85, result); + Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_THRESHOLD, result); mUpdateHandler.sendMessage(msg); } + } + + private static class Threshold { + public float compute(Bitmap bitmap) { + Bitmap grayscale = toGrayscale(bitmap); + int[] histogram = getHistogram(grayscale); + boolean isSolidColor = isSolidColor(grayscale, histogram); + + // We will see gray wallpaper during the transition if solid color wallpaper is set, + // please refer to b/130360362#comment16. + // As a result, we use Percentile85 rather than Otsus if a solid color wallpaper is set. + ThresholdAlgorithm algorithm = isSolidColor ? new Percentile85() : new Otsus(); + return algorithm.compute(grayscale, histogram); + } - private int[] processHistogram(Bitmap bitmap) { + private Bitmap toGrayscale(Bitmap bitmap) { int width = bitmap.getWidth(); int height = bitmap.getHeight(); - Bitmap target = Bitmap.createBitmap(width, height, bitmap.getConfig()); - Canvas canvas = new Canvas(target); + Bitmap grayscale = Bitmap.createBitmap(width, height, bitmap.getConfig()); + Canvas canvas = new Canvas(grayscale); ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX); Paint paint = new Paint(); paint.setColorFilter(new ColorMatrixColorFilter(cm)); canvas.drawBitmap(bitmap, new Matrix(), paint); + return grayscale; + } + + private int[] getHistogram(Bitmap grayscale) { + int width = grayscale.getWidth(); + int height = grayscale.getHeight(); + // TODO: Fine tune the performance here, tracking on b/123615079. int[] histogram = new int[256]; for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { - int pixel = target.getPixel(col, row); + int pixel = grayscale.getPixel(col, row); int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel); histogram[y]++; } @@ -121,8 +143,29 @@ class ImageProcessHelper { return histogram; } - private float computePercentile85(Bitmap bitmap, int[] histogram) { - float per85 = DEFAULT_PER85; + private boolean isSolidColor(Bitmap bitmap, int[] histogram) { + boolean solidColor = false; + int pixels = bitmap.getWidth() * bitmap.getHeight(); + + // In solid color case, only one element of histogram has value, + // which is pixel counts and the value of other elements should be 0. + for (int value : histogram) { + if (value != 0 && value != pixels) { + break; + } + if (value == pixels) { + solidColor = true; + break; + } + } + return solidColor; + } + } + + private static class Percentile85 implements ThresholdAlgorithm { + @Override + public float compute(Bitmap bitmap, int[] histogram) { + float per85 = DEFAULT_THRESHOLD; int pixelCount = bitmap.getWidth() * bitmap.getHeight(); float[] acc = new float[256]; for (int i = 0; i < acc.length; i++) { @@ -141,4 +184,51 @@ class ImageProcessHelper { return per85; } } + + private static class Otsus implements ThresholdAlgorithm { + @Override + public float compute(Bitmap bitmap, int[] histogram) { + float threshold = DEFAULT_OTSU_THRESHOLD; + float maxVariance = 0; + float pixelCount = bitmap.getWidth() * bitmap.getHeight(); + float[] w = new float[2]; + float[] m = new float[2]; + float[] u = new float[2]; + + for (int i = 0; i < histogram.length; i++) { + m[1] += i * histogram[i]; + } + + w[1] = pixelCount; + for (int tonalValue = 0; tonalValue < histogram.length; tonalValue++) { + float dU; + float variance; + float numPixels = histogram[tonalValue]; + float tmp = numPixels * tonalValue; + w[0] += numPixels; + w[1] -= numPixels; + + if (w[0] == 0 || w[1] == 0) { + continue; + } + + m[0] += tmp; + m[1] -= tmp; + u[0] = m[0] / w[0]; + u[1] = m[1] / w[1]; + dU = u[0] - u[1]; + variance = w[0] * w[1] * dU * dU; + + if (variance > maxVariance) { + threshold = (tonalValue + 1f) / histogram.length; + maxVariance = variance; + } + } + return threshold; + } + } + + private interface ThresholdAlgorithm { + float compute(Bitmap bitmap, int[] histogram); + } } diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java index 464cbe33c5f0..5bbfe84b6319 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java @@ -78,8 +78,8 @@ public class ImageWallpaperRenderer implements GLSurfaceView.Renderer, mBitmap = mWallpaperManager.getBitmap(); mBitmapWidth = mBitmap.getWidth(); mBitmapHeight = mBitmap.getHeight(); - // Compute per85 as transition threshold, this is an async work. - mImageProcessHelper.startComputingPercentile85(mBitmap); + // Compute threshold of the image, this is an async work. + mImageProcessHelper.start(mBitmap); mWallpaperManager.forgetLoadedWallpaper(); } } @@ -108,13 +108,13 @@ public class ImageWallpaperRenderer implements GLSurfaceView.Renderer, @Override public void onDrawFrame(GL10 gl) { - float per85 = mImageProcessHelper.getPercentile85(); + float threshold = mImageProcessHelper.getThreshold(); float reveal = mImageRevealHelper.getReveal(); glClear(GL_COLOR_BUFFER_BIT); glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), 1); - glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), per85); + glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), threshold); glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal); scaleViewport(reveal); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java index e352b58ea552..4571ef3ef4ef 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java @@ -77,6 +77,7 @@ public class QSCarrierGroup extends LinearLayout implements @Override public void onClick(View v) { + if (!v.isVisibleToUser()) return; mActivityStarter.postStartActivityDismissingKeyguard(new Intent( Settings.ACTION_WIRELESS_SETTINGS), 0); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 9431f20ba764..42c616c054f8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -524,7 +524,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements if (v == mClockView) { mActivityStarter.postStartActivityDismissingKeyguard(new Intent( AlarmClock.ACTION_SHOW_ALARMS), 0); - } else if (v == mNextAlarmContainer) { + } else if (v == mNextAlarmContainer && mNextAlarmContainer.isVisibleToUser()) { if (mNextAlarm.getShowIntent() != null) { mActivityStarter.postStartActivityDismissingKeyguard( mNextAlarm.getShowIntent()); @@ -545,7 +545,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0); mHost.collapsePanels(); }); - } else if (v == mRingerContainer) { + } else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) { mActivityStarter.postStartActivityDismissingKeyguard(new Intent( Settings.ACTION_SOUND_SETTINGS), 0); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index ca040762047c..9282a2e3b312 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -144,7 +144,8 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { mContext.getString(R.string.accessibility_bluetooth_name, state.label) + ", " + state.secondaryLabel; } else if (state.isTransient) { - state.icon = ResourceIcon.get(R.drawable.ic_bluetooth_transient_animation); + state.icon = ResourceIcon.get( + com.android.internal.R.drawable.ic_bluetooth_transient_animation); state.contentDescription = state.secondaryLabel; } else { state.icon = diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index 5e6f18e511d9..001e09406e3a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -130,7 +130,8 @@ public class HotspotTile extends QSTileImpl<BooleanState> { state.isTransient = isTransient; state.slash.isSlashed = !state.value && !state.isTransient; if (state.isTransient) { - state.icon = ResourceIcon.get(R.drawable.ic_hotspot_transient_animation); + state.icon = ResourceIcon.get( + com.android.internal.R.drawable.ic_hotspot_transient_animation); } state.expandedAccessibilityClassName = Switch.class.getName(); state.contentDescription = state.label; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index 15df1f161159..0e7362c3d081 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -186,7 +186,8 @@ public class WifiTile extends QSTileImpl<SignalState> { final StringBuffer minimalContentDescription = new StringBuffer(); final Resources r = mContext.getResources(); if (isTransient) { - state.icon = ResourceIcon.get(R.drawable.ic_signal_wifi_transient_animation); + state.icon = ResourceIcon.get( + com.android.internal.R.drawable.ic_signal_wifi_transient_animation); state.label = r.getString(R.string.quick_settings_wifi_label); } else if (!state.value) { state.slash.isSlashed = true; diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 4fb4999b1a5e..78c7cd406ba1 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -17,6 +17,7 @@ package com.android.systemui.recents; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; +import static android.view.Display.DEFAULT_DISPLAY; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; @@ -29,7 +30,6 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WIN import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; import android.annotation.FloatRange; import android.app.ActivityTaskManager; @@ -477,7 +477,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } - public void setSystemUiStateFlag(int flag, boolean enabled) { + public void setSystemUiStateFlag(int flag, boolean enabled, int displayId) { + if (displayId != DEFAULT_DISPLAY) { + // Ignore non-default displays for now + return; + } + int newState = mSysUiStateFlags; if (enabled) { newState |= flag; @@ -502,8 +507,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis && statusBar.getPanel().isFullyExpanded(); final boolean bouncerShowing = statusBar != null && statusBar.isBouncerShowing(); mSysUiStateFlags = 0; - mSysUiStateFlags |= ActivityManagerWrapper.getInstance().isScreenPinningActive() - ? SYSUI_STATE_SCREEN_PINNING : 0; mSysUiStateFlags |= (navBarFragment != null && !navBarFragment.isNavBarWindowVisible()) ? SYSUI_STATE_NAV_BAR_HIDDEN : 0; mSysUiStateFlags |= panelExpanded diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java index ade903d7e272..c3c0d63f66c4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java +++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java @@ -16,7 +16,6 @@ package com.android.systemui.recents; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE; import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE; @@ -139,7 +138,6 @@ public class ScreenPinningRequest implements View.OnClickListener, if (v.getId() == R.id.screen_pinning_ok_button || mRequestWindow == v) { try { ActivityTaskManager.getService().startSystemLockTaskMode(taskId); - mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_SCREEN_PINNING, true); } catch (RemoteException e) {} } clearPrompt(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index a688f36b47a6..f97be1ea8358 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -78,7 +78,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< private static final int MSG_COLLAPSE_PANELS = 4 << MSG_SHIFT; private static final int MSG_EXPAND_SETTINGS = 5 << MSG_SHIFT; private static final int MSG_SET_SYSTEMUI_VISIBILITY = 6 << MSG_SHIFT; - private static final int MSG_TOP_APP_WINDOW_CHANGED = 7 << MSG_SHIFT; + private static final int MSG_DISPLAY_READY = 7 << MSG_SHIFT; private static final int MSG_SHOW_IME_BUTTON = 8 << MSG_SHIFT; private static final int MSG_TOGGLE_RECENT_APPS = 9 << MSG_SHIFT; private static final int MSG_PRELOAD_RECENT_APPS = 10 << MSG_SHIFT; @@ -115,7 +115,6 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT; private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT; private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT; - private static final int MSG_DISPLAY_READY = 47 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -175,21 +174,14 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< * @param fullscreenStackBounds The current bounds of the fullscreen stack, in screen * coordinates. * @param dockedStackBounds The current bounds of the docked stack, in screen coordinates. + * @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME. */ default void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, - int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { + int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, + boolean navbarColorManagedByIme) { } /** - * Called to notify top app window changes. - * @see IStatusBar#topAppWindowChanged(int, boolean) - * - * @param displayId The id of the display to notify. - * @param visible {@code true} to show menu button. - */ - default void topAppWindowChanged(int displayId, boolean visible) { } - - /** * Called to notify IME window status changes. * * @param displayId The id of the display to notify. @@ -459,7 +451,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< @Override public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, - int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { + int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, + boolean navbarColorManagedByIme) { synchronized (mLock) { // Don't coalesce these, since it might have one time flags set such as // STATUS_BAR_UNHIDE which might get lost. @@ -469,6 +462,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< args.argi3 = fullscreenStackVis; args.argi4 = dockedStackVis; args.argi5 = mask; + args.argi6 = navbarColorManagedByIme ? 1 : 0; args.arg1 = fullscreenStackBounds; args.arg2 = dockedStackBounds; mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget(); @@ -476,13 +470,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } @Override - public void topAppWindowChanged(int displayId, boolean menuVisible) { - synchronized (mLock) { - mHandler.removeMessages(MSG_TOP_APP_WINDOW_CHANGED); - mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, - displayId, menuVisible ? 1 : 0, null).sendToTarget(); - } - } + public void topAppWindowChanged(int displayId, boolean menuVisible) { } @Override public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, @@ -879,15 +867,11 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< args = (SomeArgs) msg.obj; for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).setSystemUiVisibility(args.argi1, args.argi2, args.argi3, - args.argi4, args.argi5, (Rect) args.arg1, (Rect) args.arg2); + args.argi4, args.argi5, (Rect) args.arg1, (Rect) args.arg2, + args.argi6 == 1); } args.recycle(); break; - case MSG_TOP_APP_WINDOW_CHANGED: - for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).topAppWindowChanged(msg.arg1, msg.arg2 != 0); - } - break; case MSG_SHOW_IME_BUTTON: args = (SomeArgs) msg.obj; handleShowImeButton(args.argi1 /* displayId */, (IBinder) args.arg1 /* token */, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt index b7ae4edc1522..4b2d13121f6b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt @@ -16,7 +16,6 @@ package com.android.systemui.statusbar -import android.annotation.ColorInt import android.content.Context import android.graphics.Bitmap import android.graphics.Canvas @@ -28,6 +27,7 @@ import android.renderscript.RenderScript import android.renderscript.ScriptIntrinsicBlur import android.util.MathUtils import com.android.internal.graphics.ColorUtils +import com.android.systemui.statusbar.notification.MediaNotificationProcessor import javax.inject.Inject import javax.inject.Singleton @@ -42,7 +42,7 @@ class MediaArtworkProcessor @Inject constructor() { private val mTmpSize = Point() private var mArtworkCache: Bitmap? = null - fun processArtwork(context: Context, artwork: Bitmap, @ColorInt color: Int): Bitmap { + fun processArtwork(context: Context, artwork: Bitmap): Bitmap { if (mArtworkCache != null) { return mArtworkCache!! } @@ -71,13 +71,15 @@ class MediaArtworkProcessor @Inject constructor() { blur.forEach(output) output.copyTo(outBitmap) + val swatch = MediaNotificationProcessor.findBackgroundSwatch(artwork) + input.destroy() output.destroy() inBitmap.recycle() blur.destroy() val canvas = Canvas(outBitmap) - canvas.drawColor(ColorUtils.setAlphaComponent(color, COLOR_ALPHA)) + canvas.drawColor(ColorUtils.setAlphaComponent(swatch.rgb, COLOR_ALPHA)) return outBitmap } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 1615e657d498..b9e0c60fabeb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -21,11 +21,11 @@ import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_MEDIA_FAKE_AR import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_WALLPAPER; import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK; +import android.annotation.MainThread; import android.annotation.Nullable; import android.app.Notification; import android.content.Context; import android.graphics.Bitmap; -import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -35,11 +35,13 @@ import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; +import android.os.AsyncTask; import android.os.Handler; import android.os.Trace; import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; +import android.util.ArraySet; import android.util.Log; import android.view.View; import android.widget.ImageView; @@ -64,8 +66,10 @@ import com.android.systemui.statusbar.policy.KeyguardMonitor; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; +import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; @@ -108,6 +112,7 @@ public class NotificationMediaManager implements Dumpable { private final MediaSessionManager mMediaSessionManager; private final ArrayList<MediaListener> mMediaListeners; private final MediaArtworkProcessor mMediaArtworkProcessor; + private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>(); protected NotificationPresenter mPresenter; private MediaController mMediaController; @@ -449,28 +454,37 @@ public class NotificationMediaManager implements Dumpable { + " state=" + mStatusBarStateController.getState()); } - Drawable artworkDrawable = null; + Bitmap artworkBitmap = null; if (mediaMetadata != null) { - Bitmap artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART); + artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART); if (artworkBitmap == null) { artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); - // might still be null } - if (artworkBitmap != null) { - int notificationColor; - synchronized (mEntryManager.getNotificationData()) { - NotificationEntry entry = mEntryManager.getNotificationData() - .get(mMediaNotificationKey); - if (entry == null || entry.getRow() == null) { - notificationColor = Color.TRANSPARENT; - } else { - notificationColor = entry.getRow().calculateBgColor(); - } - } - Bitmap bmp = mMediaArtworkProcessor.processArtwork(mContext, artworkBitmap, - notificationColor); - artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp); + } + + // Process artwork on a background thread and send the resulting bitmap to + // finishUpdateMediaMetaData. + if (metaDataChanged) { + for (AsyncTask<?, ?, ?> task : mProcessArtworkTasks) { + task.cancel(true); } + mProcessArtworkTasks.clear(); + } + if (artworkBitmap != null) { + mProcessArtworkTasks.add(new ProcessArtworkTask(this, metaDataChanged, + allowEnterAnimation).execute(artworkBitmap)); + } else { + finishUpdateMediaMetaData(metaDataChanged, allowEnterAnimation, null); + } + + Trace.endSection(); + } + + private void finishUpdateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation, + @Nullable Bitmap bmp) { + Drawable artworkDrawable = null; + if (bmp != null) { + artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp); } boolean allowWhenShade = false; if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) { @@ -598,7 +612,6 @@ public class NotificationMediaManager implements Dumpable { } } } - Trace.endSection(); } public void setup(BackDropView backdrop, ImageView backdropFront, ImageView backdropBack, @@ -629,6 +642,61 @@ public class NotificationMediaManager implements Dumpable { } }; + private Bitmap processArtwork(Bitmap artwork) { + return mMediaArtworkProcessor.processArtwork(mContext, artwork); + } + + @MainThread + private void removeTask(AsyncTask<?, ?, ?> task) { + mProcessArtworkTasks.remove(task); + } + + /** + * {@link AsyncTask} to prepare album art for use as backdrop on lock screen. + */ + private static final class ProcessArtworkTask extends AsyncTask<Bitmap, Void, Bitmap> { + + private final WeakReference<NotificationMediaManager> mManagerRef; + private final boolean mMetaDataChanged; + private final boolean mAllowEnterAnimation; + + ProcessArtworkTask(NotificationMediaManager manager, boolean changed, + boolean allowAnimation) { + mManagerRef = new WeakReference<>(manager); + mMetaDataChanged = changed; + mAllowEnterAnimation = allowAnimation; + } + + @Override + protected Bitmap doInBackground(Bitmap... bitmaps) { + NotificationMediaManager manager = mManagerRef.get(); + if (manager == null || bitmaps.length == 0 || isCancelled()) { + return null; + } + return manager.processArtwork(bitmaps[0]); + } + + @Override + protected void onPostExecute(@Nullable Bitmap result) { + NotificationMediaManager manager = mManagerRef.get(); + if (manager != null && !isCancelled()) { + manager.removeTask(this); + manager.finishUpdateMediaMetaData(mMetaDataChanged, mAllowEnterAnimation, result); + } + } + + @Override + protected void onCancelled(Bitmap result) { + if (result != null) { + result.recycle(); + } + NotificationMediaManager manager = mManagerRef.get(); + if (manager != null) { + manager.removeTask(this); + } + } + } + public interface MediaListener { void onMetadataChanged(MediaMetadata metadata); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 2793b2a0f009..fe8c6b7c1e2c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -51,6 +51,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dumpable; +import com.android.systemui.R; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -273,10 +274,20 @@ public class NotificationRemoteInputManager implements Dumpable { notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override + public void onPreEntryUpdated(NotificationEntry entry) { + // Mark smart replies as sent whenever a notification is updated - otherwise the + // smart replies are never marked as sent. + mSmartReplyController.stopSending(entry); + } + + @Override public void onEntryRemoved( @Nullable NotificationEntry entry, NotificationVisibility visibility, boolean removedByUser) { + // We're removing the notification, the smart controller can forget about it. + mSmartReplyController.stopSending(entry); + if (removedByUser && entry != null) { onPerformRemoveNotification(entry, entry.key); } @@ -348,24 +359,18 @@ public class NotificationRemoteInputManager implements Dumpable { ViewParent p = view.getParent(); RemoteInputView riv = null; + ExpandableNotificationRow row = null; while (p != null) { if (p instanceof View) { View pv = (View) p; if (pv.isRootNamespace()) { riv = findRemoteInputView(pv); + row = (ExpandableNotificationRow) pv.getTag(R.id.row_tag_for_content_view); break; } } p = p.getParent(); } - ExpandableNotificationRow row = null; - while (p != null) { - if (p instanceof ExpandableNotificationRow) { - row = (ExpandableNotificationRow) p; - break; - } - p = p.getParent(); - } if (row == null) { return false; @@ -391,10 +396,13 @@ public class NotificationRemoteInputManager implements Dumpable { if (riv == null) { return false; } - if (!row.getPrivateLayout().getExpandedChild().isShown()) { - mCallback.onMakeExpandedVisibleForRemoteInput(row, view); - return true; - } + } + if (riv == row.getPrivateLayout().getExpandedRemoteInput() + && !row.getPrivateLayout().getExpandedChild().isShown()) { + // The expanded layout is selected, but it's not shown yet, let's wait on it to + // show before we do the animation. + mCallback.onMakeExpandedVisibleForRemoteInput(row, view); + return true; } int width = view.getWidth(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java index 333239e77d1b..0d9f4e7b909d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java @@ -30,6 +30,7 @@ import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.SyncRtSurfaceTransactionApplier; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; +import android.view.View; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.systemui.Interpolators; @@ -79,11 +80,12 @@ public class ActivityLaunchAnimator { } public RemoteAnimationAdapter getLaunchAnimation( - ExpandableNotificationRow sourceNotification, boolean occluded) { - if (!mCallback.areLaunchAnimationsEnabled() || occluded) { + View sourceView, boolean occluded) { + if (!(sourceView instanceof ExpandableNotificationRow) || !mCallback.areLaunchAnimationsEnabled() || occluded) { return null; } - AnimationRunner animationRunner = new AnimationRunner(sourceNotification); + AnimationRunner animationRunner = new AnimationRunner( + (ExpandableNotificationRow) sourceView); return new RemoteAnimationAdapter(animationRunner, ANIMATION_DURATION, ANIMATION_DURATION - 150 /* statusBarTransitionDelay */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java index ab94008d656a..5d1ab4fd9ad2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java @@ -69,8 +69,7 @@ public class MediaNotificationProcessor { private static final int RESIZE_BITMAP_AREA = 150 * 150; private final ImageGradientColorizer mColorizer; private final Context mContext; - private float[] mFilteredBackgroundHsl = null; - private Palette.Filter mBlackWhiteFilter = (rgb, hsl) -> !isWhiteOrBlack(hsl); + private final Palette.Filter mBlackWhiteFilter = (rgb, hsl) -> !isWhiteOrBlack(hsl); /** * The context of the notification. This is the app context of the package posting the @@ -121,23 +120,21 @@ public class MediaNotificationProcessor { drawable.setBounds(0, 0, width, height); drawable.draw(canvas); - // for the background we only take the left side of the image to ensure - // a smooth transition - Palette.Builder paletteBuilder = Palette.from(bitmap) - .setRegion(0, 0, bitmap.getWidth() / 2, bitmap.getHeight()) - .clearFilters() // we want all colors, red / white / black ones too! - .resizeBitmapArea(RESIZE_BITMAP_AREA); + Palette.Builder paletteBuilder = generateArtworkPaletteBuilder(bitmap); Palette palette = paletteBuilder.generate(); - backgroundColor = findBackgroundColorAndFilter(palette); + Palette.Swatch backgroundSwatch = findBackgroundSwatch(palette); + backgroundColor = backgroundSwatch.getRgb(); // we want most of the full region again, slightly shifted to the right float textColorStartWidthFraction = 0.4f; paletteBuilder.setRegion((int) (bitmap.getWidth() * textColorStartWidthFraction), 0, bitmap.getWidth(), bitmap.getHeight()); - if (mFilteredBackgroundHsl != null) { + // We're not filtering on white or black + if (!isWhiteOrBlack(backgroundSwatch.getHsl())) { + final float backgroundHue = backgroundSwatch.getHsl()[0]; paletteBuilder.addFilter((rgb, hsl) -> { // at least 10 degrees hue difference - float diff = Math.abs(hsl[0] - mFilteredBackgroundHsl[0]); + float diff = Math.abs(hsl[0] - backgroundHue); return diff > 10 && diff < 350; }); } @@ -244,18 +241,31 @@ public class MediaNotificationProcessor { && (swatch.getPopulation() / (float) RESIZE_BITMAP_AREA > MINIMUM_IMAGE_FRACTION); } - private int findBackgroundColorAndFilter(Palette palette) { + /** + * Finds an appropriate background swatch from media artwork. + * + * @param artwork Media artwork + * @return Swatch that should be used as the background of the media notification. + */ + public static Palette.Swatch findBackgroundSwatch(Bitmap artwork) { + return findBackgroundSwatch(generateArtworkPaletteBuilder(artwork).generate()); + } + + /** + * Finds an appropriate background swatch from the palette of media artwork. + * + * @param palette Artwork palette, should be obtained from {@link generateArtworkPaletteBuilder} + * @return Swatch that should be used as the background of the media notification. + */ + private static Palette.Swatch findBackgroundSwatch(Palette palette) { // by default we use the dominant palette Palette.Swatch dominantSwatch = palette.getDominantSwatch(); if (dominantSwatch == null) { - // We're not filtering on white or black - mFilteredBackgroundHsl = null; - return Color.WHITE; + return new Palette.Swatch(Color.WHITE, 100); } if (!isWhiteOrBlack(dominantSwatch.getHsl())) { - mFilteredBackgroundHsl = dominantSwatch.getHsl(); - return dominantSwatch.getRgb(); + return dominantSwatch; } // Oh well, we selected black or white. Lets look at the second color! List<Palette.Swatch> swatches = palette.getSwatches(); @@ -270,38 +280,51 @@ public class MediaNotificationProcessor { } } if (second == null) { - // We're not filtering on white or black - mFilteredBackgroundHsl = null; - return dominantSwatch.getRgb(); + return dominantSwatch; } if (dominantSwatch.getPopulation() / highestNonWhitePopulation > POPULATION_FRACTION_FOR_WHITE_OR_BLACK) { // The dominant swatch is very dominant, lets take it! // We're not filtering on white or black - mFilteredBackgroundHsl = null; - return dominantSwatch.getRgb(); + return dominantSwatch; } else { - mFilteredBackgroundHsl = second.getHsl(); - return second.getRgb(); + return second; } } - private boolean isWhiteOrBlack(float[] hsl) { - return isBlack(hsl) || isWhite(hsl); + /** + * Generate a palette builder for media artwork. + * + * For producing a smooth background transition, the palette is extracted from only the left + * side of the artwork. + * + * @param artwork Media artwork + * @return Builder that generates the {@link Palette} for the media artwork. + */ + private static Palette.Builder generateArtworkPaletteBuilder(Bitmap artwork) { + // for the background we only take the left side of the image to ensure + // a smooth transition + return Palette.from(artwork) + .setRegion(0, 0, artwork.getWidth() / 2, artwork.getHeight()) + .clearFilters() // we want all colors, red / white / black ones too! + .resizeBitmapArea(RESIZE_BITMAP_AREA); } + private static boolean isWhiteOrBlack(float[] hsl) { + return isBlack(hsl) || isWhite(hsl); + } /** * @return true if the color represents a color which is close to black. */ - private boolean isBlack(float[] hslColor) { + private static boolean isBlack(float[] hslColor) { return hslColor[2] <= BLACK_MAX_LIGHTNESS; } /** * @return true if the color represents a color which is close to white. */ - private boolean isWhite(float[] hslColor) { + private static boolean isWhite(float[] hslColor) { return hslColor[2] >= WHITE_MIN_LIGHTNESS; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index b81d81438ea3..ad745f5ab276 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -542,6 +542,12 @@ public class NotificationContentView extends FrameLayout { } @Override + public void onViewAdded(View child) { + super.onViewAdded(child); + child.setTag(R.id.row_tag_for_content_view, mContainingNotification); + } + + @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); updateVisibility(); @@ -1893,4 +1899,8 @@ public class NotificationContentView extends FrameLayout { } pw.println(); } + + public RemoteInputView getExpandedRemoteInput() { + return mExpandedRemoteInput; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java index fdf8ccee3f62..5912cd7b6433 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java @@ -95,7 +95,8 @@ public class AutoHideController implements CommandQueue.Callbacks { @Override public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, - int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { + int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, + boolean navbarColorManagedByIme) { if (displayId != mDisplayId) { return; } @@ -119,7 +120,7 @@ public class AutoHideController implements CommandQueue.Callbacks { if (mSystemUiVisibility != newVal) { mCommandQueue.setSystemUiVisibility(mDisplayId, mSystemUiVisibility, fullscreenStackVis, dockedStackVis, mask, fullscreenStackBounds, - dockedStackBounds); + dockedStackBounds, navbarColorManagedByIme); } notifySystemUiVisibilityChanged(mSystemUiVisibility); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java index b590ca7f4df0..b0b656a1a951 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java @@ -79,6 +79,9 @@ public class LightBarController implements BatteryController.BatteryStateChangeC private final Rect mLastDockedBounds = new Rect(); private boolean mQsCustomizing; + private boolean mDirectReplying; + private boolean mNavbarColorManagedByIme; + @Inject public LightBarController(Context ctx, DarkIconDispatcher darkIconDispatcher, BatteryController batteryController) { @@ -100,7 +103,7 @@ public class LightBarController implements BatteryController.BatteryStateChangeC public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged, - int statusBarMode) { + int statusBarMode, boolean navbarColorManagedByIme) { int oldFullscreen = mFullscreenStackVisibility; int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask); int diffFullscreen = newFullscreen ^ oldFullscreen; @@ -122,12 +125,13 @@ public class LightBarController implements BatteryController.BatteryStateChangeC mFullscreenStackVisibility = newFullscreen; mDockedStackVisibility = newDocked; mLastStatusBarMode = statusBarMode; + mNavbarColorManagedByIme = navbarColorManagedByIme; mLastFullscreenBounds.set(fullscreenStackBounds); mLastDockedBounds.set(dockedStackBounds); } public void onNavigationVisibilityChanged(int vis, int mask, boolean nbModeChanged, - int navigationBarMode) { + int navigationBarMode, boolean navbarColorManagedByIme) { int oldVis = mSystemUiVisibility; int newVis = (oldVis & ~mask) | (vis & mask); int diffVis = newVis ^ oldVis; @@ -136,21 +140,24 @@ public class LightBarController implements BatteryController.BatteryStateChangeC boolean last = mNavigationLight; mHasLightNavigationBar = isLight(vis, navigationBarMode, View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR); - mNavigationLight = mHasLightNavigationBar && !mForceDarkForScrim && !mQsCustomizing; + mNavigationLight = mHasLightNavigationBar + && (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim) + && !mQsCustomizing; if (mNavigationLight != last) { updateNavigation(); } } mSystemUiVisibility = newVis; mLastNavigationBarMode = navigationBarMode; + mNavbarColorManagedByIme = navbarColorManagedByIme; } private void reevaluate() { onSystemUiVisibilityChanged(mFullscreenStackVisibility, mDockedStackVisibility, 0 /* mask */, mLastFullscreenBounds, mLastDockedBounds, - true /* sbModeChange*/, mLastStatusBarMode); + true /* sbModeChange*/, mLastStatusBarMode, mNavbarColorManagedByIme); onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */, true /* nbModeChanged */, - mLastNavigationBarMode); + mLastNavigationBarMode, mNavbarColorManagedByIme); } public void setQsCustomizing(boolean customizing) { @@ -159,6 +166,16 @@ public class LightBarController implements BatteryController.BatteryStateChangeC reevaluate(); } + /** + * Sets whether the direct-reply is in use or not. + * @param directReplying {@code true} when the direct-reply is in-use. + */ + public void setDirectReplying(boolean directReplying) { + if (mDirectReplying == directReplying) return; + mDirectReplying = directReplying; + reevaluate(); + } + public void setScrimState(ScrimState scrimState, float scrimBehindAlpha, GradientColors scrimInFrontColor) { boolean forceDarkForScrimLast = mForceDarkForScrim; @@ -260,7 +277,9 @@ public class LightBarController implements BatteryController.BatteryStateChangeC pw.print(" mLastNavigationBarMode="); pw.println(mLastNavigationBarMode); pw.print(" mForceDarkForScrim="); pw.print(mForceDarkForScrim); - pw.print(" mQsCustomizing="); pw.println(mQsCustomizing); + pw.print(" mQsCustomizing="); pw.print(mQsCustomizing); + pw.print(" mDirectReplying="); pw.println(mDirectReplying); + pw.print(" mNavbarColorManagedByIme="); pw.println(mNavbarColorManagedByIme); pw.println(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index a831a5d29a79..3b3bd218a8e6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; import android.content.Context; @@ -27,6 +28,7 @@ import android.graphics.drawable.Animatable2; import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; import android.hardware.biometrics.BiometricSourceType; +import android.os.Handler; import android.util.AttributeSet; import android.view.ViewGroup; import android.view.accessibility.AccessibilityNodeInfo; @@ -66,6 +68,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final AccessibilityController mAccessibilityController; private final DockManager mDockManager; + private final Handler mMainHandler; private int mLastState = 0; private boolean mTransientBiometricsError; @@ -82,6 +85,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange private boolean mLastBouncerVisible; private int mIconColor; private float mDozeAmount; + private int mIconRes; + private boolean mWasPulsingOnThisFrame; private final Runnable mDrawOffTimeout = () -> update(true /* forceUpdate */); private final DockManager.DockEventListener mDockEventListener = @@ -133,7 +138,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange StatusBarStateController statusBarStateController, ConfigurationController configurationController, AccessibilityController accessibilityController, - @Nullable DockManager dockManager) { + @Nullable DockManager dockManager, + @Named(MAIN_HANDLER_NAME) Handler mainHandler) { super(context, attrs); mContext = context; mUnlockMethodCache = UnlockMethodCache.getInstance(context); @@ -142,6 +148,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mConfigurationController = configurationController; mStatusBarStateController = statusBarStateController; mDockManager = dockManager; + mMainHandler = mainHandler; } @Override @@ -214,39 +221,36 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mPulsing, mLastDozing, mDozing, mBouncerVisible); boolean isAnim = iconAnimRes != -1; - Drawable icon; - if (isAnim) { - // Load the animation resource. - icon = mContext.getDrawable(iconAnimRes); - } else { - // Load the static icon resource based on the current state. - icon = getIconForState(state); - } - - final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable - ? (AnimatedVectorDrawable) icon - : null; - setImageDrawable(icon, false); - updateDarkTint(); - if (mIsFaceUnlockState) { - announceForAccessibility(getContext().getString( - R.string.accessibility_scanning_face)); - } + int iconRes = isAnim ? iconAnimRes : getIconForState(state); + if (iconRes != mIconRes) { + mIconRes = iconRes; + + Drawable icon = mContext.getDrawable(iconRes); + final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable + ? (AnimatedVectorDrawable) icon + : null; + setImageDrawable(icon, false); + if (mIsFaceUnlockState) { + announceForAccessibility(getContext().getString( + R.string.accessibility_scanning_face)); + } - if (animation != null && isAnim) { - animation.forceAnimationOnUI(); - animation.clearAnimationCallbacks(); - animation.registerAnimationCallback(new Animatable2.AnimationCallback() { - @Override - public void onAnimationEnd(Drawable drawable) { - if (getDrawable() == animation && state == getState() - && doesAnimationLoop(iconAnimRes)) { - animation.start(); + if (animation != null && isAnim) { + animation.forceAnimationOnUI(); + animation.clearAnimationCallbacks(); + animation.registerAnimationCallback(new Animatable2.AnimationCallback() { + @Override + public void onAnimationEnd(Drawable drawable) { + if (getDrawable() == animation && state == getState() + && doesAnimationLoop(iconAnimRes)) { + animation.start(); + } } - } - }); - animation.start(); + }); + animation.start(); + } } + updateDarkTint(); if (isAnim && !mLastScreenOn) { removeCallbacks(mDrawOffTimeout); @@ -300,7 +304,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange } } - private Drawable getIconForState(int state) { + private int getIconForState(int state) { int iconRes; switch (state) { case STATE_LOCKED: @@ -318,25 +322,27 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange throw new IllegalArgumentException(); } - return mContext.getDrawable(iconRes); + return iconRes; } private boolean doesAnimationLoop(int resourceId) { return resourceId == com.android.internal.R.anim.lock_scanning; } - private static int getAnimationResForTransition(int oldState, int newState, + private int getAnimationResForTransition(int oldState, int newState, boolean wasPulsing, boolean pulsing, boolean wasDozing, boolean dozing, boolean bouncerVisible) { // Never animate when screen is off - if (dozing && !pulsing) { + if (dozing && !pulsing && !mWasPulsingOnThisFrame) { return -1; } boolean isError = oldState != STATE_BIOMETRICS_ERROR && newState == STATE_BIOMETRICS_ERROR; boolean justUnlocked = oldState != STATE_LOCK_OPEN && newState == STATE_LOCK_OPEN; boolean justLocked = oldState == STATE_LOCK_OPEN && newState == STATE_LOCKED; + boolean nowPulsing = !wasPulsing && pulsing; + boolean turningOn = wasDozing && !dozing && !mWasPulsingOnThisFrame; if (isError) { return com.android.internal.R.anim.lock_to_error; @@ -346,7 +352,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange return com.android.internal.R.anim.lock_lock; } else if (newState == STATE_SCANNING_FACE && bouncerVisible) { return com.android.internal.R.anim.lock_scanning; - } else if (!wasPulsing && pulsing && newState != STATE_LOCK_OPEN) { + } else if ((nowPulsing || turningOn) && newState != STATE_LOCK_OPEN) { return com.android.internal.R.anim.lock_in; } return -1; @@ -377,6 +383,12 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange */ public void setPulsing(boolean pulsing) { mPulsing = pulsing; + if (!mPulsing) { + mWasPulsingOnThisFrame = true; + mMainHandler.post(() -> { + mWasPulsingOnThisFrame = false; + }); + } update(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index fa3377a708bc..8aa4f0382649 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -27,7 +27,6 @@ import static com.android.systemui.recents.OverviewProxyService.OverviewProxyLis import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; @@ -328,7 +327,10 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback filter.addAction(Intent.ACTION_USER_SWITCHED); getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); notifyNavigationBarScreenOn(); + mOverviewProxyService.addCallback(mOverviewProxyListener); + mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NAV_BAR_HIDDEN, + !isNavBarWindowVisible(), mDisplayId); // Currently there is no accelerometer sensor on non-default display. if (mIsOnDefaultDisplay) { @@ -447,13 +449,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } @Override - public void topAppWindowChanged(int displayId, boolean showMenu) { - if (displayId == mDisplayId && mNavigationBarView != null) { - mNavigationBarView.setMenuVisibility(showMenu); - } - } - - @Override public void setWindowState( int displayId, @WindowType int window, @WindowVisibleState int state) { if (displayId == mDisplayId @@ -464,7 +459,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state)); mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NAV_BAR_HIDDEN, - !isNavBarWindowVisible()); + !isNavBarWindowVisible(), mDisplayId); mNavigationBarView.getRotateSuggestionButton() .onNavigationBarWindowVisibilityChange(isNavBarWindowVisible()); } @@ -505,12 +500,13 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mAutoHideController.touchAutoHide(); mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */, - true /* nbModeChanged */, mNavigationBarMode); + true /* nbModeChanged */, mNavigationBarMode, false /* navbarColorManagedByIme */); } @Override public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, - int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { + int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, + boolean navbarColorManagedByIme) { if (displayId != mDisplayId) { return; } @@ -538,7 +534,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } } mLightBarController.onNavigationVisibilityChanged( - vis, mask, nbModeChanged, mNavigationBarMode); + vis, mask, nbModeChanged, mNavigationBarMode, navbarColorManagedByIme); } private @TransitionMode int computeBarMode(int oldVis, int newVis) { @@ -824,7 +820,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback activityManager.stopSystemLockTaskMode(); // When exiting refresh disabled flags. mNavigationBarView.updateNavButtonIcons(); - mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_SCREEN_PINNING, false); } } @@ -876,9 +871,10 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback boolean clickable = (flags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; boolean longClickable = (flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; mNavigationBarView.setAccessibilityButtonState(clickable, longClickable); - mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable); mOverviewProxyService.setSystemUiStateFlag( - SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable); + SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable, mDisplayId); + mOverviewProxyService.setSystemUiStateFlag( + SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable, mDisplayId); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 22636deccb33..831d882d68a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -268,8 +268,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav // Set up the context group of buttons mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container); - final ContextualButton menuButton = new ContextualButton(R.id.menu, - R.drawable.ic_sysbar_menu); final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher, R.drawable.ic_ime_switcher_default); final RotationContextButton rotateSuggestionButton = new RotationContextButton( @@ -278,7 +276,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav final ContextualButton accessibilityButton = new ContextualButton(R.id.accessibility_button, R.drawable.ic_sysbar_accessibility_button); - mContextualButtonGroup.addButton(menuButton); mContextualButtonGroup.addButton(imeSwitcherButton); if (!isGesturalMode) { mContextualButtonGroup.addButton(rotateSuggestionButton); @@ -306,7 +303,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home)); mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle)); mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps)); - mButtonDispatchers.put(R.id.menu, menuButton); mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton); mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton); mButtonDispatchers.put(R.id.rotate_suggestion, rotateSuggestionButton); @@ -406,10 +402,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav return mButtonDispatchers.get(R.id.recent_apps); } - public ButtonDispatcher getMenuButton() { - return mButtonDispatchers.get(R.id.menu); - } - public ButtonDispatcher getBackButton() { return mButtonDispatchers.get(R.id.back); } @@ -722,10 +714,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } } - public void onPanelExpandedChange(boolean expanded) { + public void onPanelExpandedChange() { updateSlippery(); mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED, - expanded); + mPanelView.isFullyExpanded(), getContext().getDisplayId()); } public void updateStates() { @@ -795,10 +787,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } } - public void setMenuVisibility(final boolean show) { - mContextualButtonGroup.setButtonVisibility(R.id.menu, show); - } - public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) { mLongClickableAccessibilityButton = longClickable; getAccessibilityButton().setLongClickable(longClickable); @@ -1154,16 +1142,14 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav visibilityToString(getCurrentView().getVisibility()), getCurrentView().getAlpha())); - pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s darkIntensity=%.2f", + pw.println(String.format(" disabled=0x%08x vertical=%s darkIntensity=%.2f", mDisabledFlags, mIsVertical ? "true" : "false", - getMenuButton().isVisible() ? "true" : "false", getLightTransitionsController().getCurrentDarkIntensity())); dumpButton(pw, "back", getBackButton()); dumpButton(pw, "home", getHomeButton()); dumpButton(pw, "rcnt", getRecentsButton()); - dumpButton(pw, "menu", getMenuButton()); dumpButton(pw, "rota", getRotateSuggestionButton()); dumpButton(pw, "a11y", getAccessibilityButton()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 7b3ddf7cfaef..1194a1d7b3f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -833,6 +833,11 @@ public class NotificationPanelView extends PanelView implements return false; } initDownStates(event); + // Do not let touches go to shade or QS if the bouncer is visible, + // but still let user swipe down to expand the panel, dismissing the bouncer. + if (mStatusBar.isBouncerShowing()) { + return true; + } if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { mIsExpansionFromHeadsUp = true; MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1); @@ -1000,6 +1005,13 @@ public class NotificationPanelView extends PanelView implements if (mBlockTouches || (mQs != null && mQs.isCustomizing())) { return false; } + + // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able to + // pull down QS or expand the shade. + if (mStatusBar.isBouncerShowingScrimmed()) { + return false; + } + initDownStates(event); // Make sure the next touch won't the blocked after the current ends. if (event.getAction() == MotionEvent.ACTION_UP diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index ce1d6384b7c0..68eba50984d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -277,7 +277,7 @@ public class PhoneStatusBarView extends PanelBar { super.panelExpansionChanged(frac, expanded); updateScrimFraction(); if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) { - mBar.getNavigationBarView().onPanelExpandedChange(expanded); + mBar.getNavigationBarView().onPanelExpandedChange(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 93db82d8e119..1fc40b42c552 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -436,7 +436,6 @@ public class StatusBar extends SystemUI implements DemoMode, public void onUserSetupChanged() { final boolean userSetup = mDeviceProvisionedController.isUserSetup( mDeviceProvisionedController.getCurrentUser()); - // STOPSHIP(kozynski, b/129405675) Remove log Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for user " + mDeviceProvisionedController.getCurrentUser()); if (MULTIUSER_DEBUG) { @@ -593,6 +592,7 @@ public class StatusBar extends SystemUI implements DemoMode, updateScrimController(); }; private ActivityIntentHelper mActivityIntentHelper; + private ShadeController mShadeController; @Override public void onActiveStateChanged(int code, int uid, String packageName, boolean active) { @@ -710,8 +710,8 @@ public class StatusBar extends SystemUI implements DemoMode, setSystemUiVisibility(mDisplayId, result.mSystemUiVisibility, result.mFullscreenStackSysUiVisibility, result.mDockedStackSysUiVisibility, - 0xffffffff, result.mFullscreenStackBounds, result.mDockedStackBounds); - topAppWindowChanged(mDisplayId, result.mMenuVisible); + 0xffffffff, result.mFullscreenStackBounds, result.mDockedStackBounds, + result.mNavbarColorManagedByIme); // StatusBarManagerService has a back up of IME token and it's restored here. setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis, result.mImeBackDisposition, result.mShowImeSwitcher); @@ -725,11 +725,10 @@ public class StatusBar extends SystemUI implements DemoMode, if (DEBUG) { Log.d(TAG, String.format( - "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x", + "init: icons=%d disabled=0x%08x lights=0x%08x imeButton=0x%08x", numIcons, result.mDisabledFlags1, result.mSystemUiVisibility, - result.mMenuVisible ? 1 : 0, result.mImeWindowVis)); } @@ -1062,7 +1061,7 @@ public class StatusBar extends SystemUI implements DemoMode, final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback = (StatusBarRemoteInputCallback) Dependency.get( NotificationRemoteInputManager.Callback.class); - final ShadeController shadeController = Dependency.get(ShadeController.class); + mShadeController = Dependency.get(ShadeController.class); final ActivityStarter activityStarter = Dependency.get(ActivityStarter.class); mNotificationActivityStarter = new StatusBarNotificationActivityStarter(mContext, @@ -1070,7 +1069,7 @@ public class StatusBar extends SystemUI implements DemoMode, mHeadsUpManager, activityStarter, mActivityLaunchAnimator, mBarService, mStatusBarStateController, mKeyguardManager, mDreamManager, mRemoteInputManager, mStatusBarRemoteInputCallback, mGroupManager, - mLockscreenUserManager, shadeController, mKeyguardMonitor, + mLockscreenUserManager, mShadeController, mKeyguardMonitor, mNotificationInterruptionStateProvider, mMetricsLogger, new LockPatternUtils(mContext), Dependency.get(MAIN_HANDLER), Dependency.get(BG_HANDLER), mActivityIntentHelper, mBubbleController); @@ -2077,7 +2076,8 @@ public class StatusBar extends SystemUI implements DemoMode, @Override // CommandQueue public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, - int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { + int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, + boolean navbarColorManagedByIme) { if (displayId != mDisplayId) { return; } @@ -2114,7 +2114,8 @@ public class StatusBar extends SystemUI implements DemoMode, } } mLightBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis, - mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode); + mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode, + navbarColorManagedByIme); } @Override @@ -2231,30 +2232,6 @@ public class StatusBar extends SystemUI implements DemoMode, return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE); } - public void setLightsOn(boolean on) { - Log.v(TAG, "setLightsOn(" + on + ")"); - if (on) { - setSystemUiVisibility(mDisplayId, 0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE, - mLastFullscreenStackBounds, mLastDockedStackBounds); - } else { - setSystemUiVisibility(mDisplayId, View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0, - View.SYSTEM_UI_FLAG_LOW_PROFILE, mLastFullscreenStackBounds, - mLastDockedStackBounds); - } - } - - @Override - public void topAppWindowChanged(int displayId, boolean showMenu) { - if (mDisplayId != displayId) return; - if (SPEW) { - Log.d(TAG, "display#" + displayId + ": " - + (showMenu ? "showing" : "hiding") + " the MENU button"); - } - - // See above re: lights-out policy for legacy apps. - if (showMenu) setLightsOn(true); - } - public static String viewInfo(View v) { return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom() + ") " + v.getWidth() + "x" + v.getHeight() + "]"; @@ -3584,7 +3561,7 @@ public class StatusBar extends SystemUI implements DemoMode, // Notify overview proxy service of the new states Dependency.get(OverviewProxyService.class).setSystemUiStateFlag(SYSUI_STATE_BOUNCER_SHOWING, - isBouncerShowing()); + isBouncerShowing(), mContext.getDisplayId()); } /** @@ -4344,6 +4321,13 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void startPendingIntentDismissingKeyguard( final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback) { + startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback, null /* row */); + } + + @Override + public void startPendingIntentDismissingKeyguard( + final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback, + View associatedView) { final boolean afterKeyguardGone = intent.isActivity() && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(), mLockscreenUserManager.getCurrentUserId()); @@ -4351,7 +4335,8 @@ public class StatusBar extends SystemUI implements DemoMode, executeActionDismissingKeyguard(() -> { try { intent.send(null, 0, null, null, null, null, getActivityOptions( - null /* animationAdapter */)); + mActivityLaunchAnimator.getLaunchAnimation(associatedView, + mShadeController.isOccluded()))); } catch (PendingIntent.CanceledException e) { // the stack trace isn't very helpful here. // Just log the exception message. @@ -4428,6 +4413,13 @@ public class StatusBar extends SystemUI implements DemoMode, } /** + * @return Whether the security bouncer from Keyguard is showing. + */ + public boolean isBouncerShowingScrimmed() { + return isBouncerShowing() && mStatusBarKeyguardViewManager.bouncerNeedsScrimming(); + } + + /** * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then * return PackageManager for mContext */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 7fe89069b438..d350c3a7ae96 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -40,7 +40,6 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.settingslib.animation.AppearAnimationUtils; import com.android.systemui.DejankUtils; import com.android.systemui.Dependency; -import com.android.systemui.SysUiServiceProvider; import com.android.systemui.SystemUIFactory; import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.DismissCallbackRegistry; @@ -192,9 +191,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback); mStatusBarStateController.addCallback(this); Dependency.get(ConfigurationController.class).addCallback(this); - mLastGesturalNav = QuickStepContract.isGesturalMode( + mGesturalNav = QuickStepContract.isGesturalMode( Dependency.get(NavigationModeController.class).addListener(this)); - mDockManager = SysUiServiceProvider.getComponent(context, DockManager.class); + mDockManager = Dependency.get(DockManager.class); if (mDockManager != null) { mDockManager.addListener(mDockEventListener); mIsDocked = mDockManager.isDocked(); @@ -484,7 +483,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // by a FLAG_DISMISS_KEYGUARD_ACTIVITY. reset(isOccluding /* hideBouncerWhenShowing*/); } - if (animate && !occluded && mShowing) { + if (animate && !occluded && mShowing && !mBouncer.isShowing()) { mStatusBar.animateKeyguardUnoccluding(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java index 471d5113a502..0865eb6ae7d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -28,6 +28,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; +import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.view.View; @@ -65,6 +66,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class); private final Context mContext; private final ActivityIntentHelper mActivityIntentHelper; + private final NotificationGroupManager mGroupManager; private View mPendingWorkRemoteInputView; private View mPendingRemoteInputView; private final ShadeController mShadeController = Dependency.get(ShadeController.class); @@ -72,11 +74,12 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, private final CommandQueue mCommandQueue; private int mDisabled2; protected BroadcastReceiver mChallengeReceiver = new ChallengeReceiver(); + private Handler mMainHandler = new Handler(); /** */ @Inject - public StatusBarRemoteInputCallback(Context context) { + public StatusBarRemoteInputCallback(Context context, NotificationGroupManager groupManager) { mContext = context; mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL, new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null); @@ -85,15 +88,15 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, mCommandQueue = getComponent(context, CommandQueue.class); mCommandQueue.addCallback(this); mActivityIntentHelper = new ActivityIntentHelper(mContext); + mGroupManager = groupManager; } @Override public void onStateChanged(int state) { if (state == StatusBarState.SHADE && mStatusBarStateController.leaveOpenOnKeyguardHide()) { if (!mStatusBarStateController.isKeyguardRequested()) { - if (mPendingRemoteInputView != null - && mPendingRemoteInputView.isAttachedToWindow()) { - mPendingRemoteInputView.post(mPendingRemoteInputView::callOnClick); + if (mPendingRemoteInputView != null) { + mMainHandler.post(mPendingRemoteInputView::callOnClick); } mPendingRemoteInputView = null; } @@ -159,6 +162,10 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, if (mKeyguardMonitor.isShowing()) { onLockedRemoteInput(row, clickedView); } else { + if (row.isChildInGroup() && !row.areChildrenExpanded()) { + // The group isn't expanded, let's make sure it's visible! + mGroupManager.toggleGroupExpansion(row.getStatusBarNotification()); + } row.setUserExpanded(true); row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index 9c7a1e34aeb7..3fc9b44f3850 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -212,13 +212,15 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC @Nullable private String generateTimeRemainingString() { - if (mEstimate == null) { - return null; - } + synchronized (mFetchCallbacks) { + if (mEstimate == null) { + return null; + } - String percentage = NumberFormat.getPercentInstance().format((double) mLevel / 100.0); - return PowerUtil.getBatteryRemainingShortStringFormatted( - mContext, mEstimate.getEstimateMillis()); + String percentage = NumberFormat.getPercentInstance().format((double) mLevel / 100.0); + return PowerUtil.getBatteryRemainingShortStringFormatted( + mContext, mEstimate.getEstimateMillis()); + } } private void updateEstimateInBackground() { @@ -230,9 +232,11 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC mFetchingEstimate = true; Dependency.get(Dependency.BG_HANDLER).post(() -> { // Only fetch the estimate if they are enabled - mEstimate = null; - if (mEstimates.isHybridNotificationEnabled()) { - updateEstimate(); + synchronized (mFetchCallbacks) { + mEstimate = null; + if (mEstimates.isHybridNotificationEnabled()) { + updateEstimate(); + } } mFetchingEstimate = false; Dependency.get(Dependency.MAIN_HANDLER).post(this::notifyEstimateFetchCallbacks); @@ -240,9 +244,8 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC } private void notifyEstimateFetchCallbacks() { - String estimate = generateTimeRemainingString(); - synchronized (mFetchCallbacks) { + String estimate = generateTimeRemainingString(); for (EstimateFetchCompletion completion : mFetchCallbacks) { completion.onBatteryRemainingEstimateRetrieved(estimate); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java index db2523e40ded..98ab3e1b16fd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java @@ -61,7 +61,6 @@ public class DeviceProvisionedControllerImpl extends CurrentUserTracker implemen mSettingsObserver = new ContentObserver(mainHandler) { @Override public void onChange(boolean selfChange, Uri uri, int userId) { - // STOPSHIP(kozynski, b/129405675) Remove log Log.d(TAG, "Setting change: " + uri); if (mUserSetupUri.equals(uri)) { notifySetupChanged(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index c1950a21d10f..43795dc08c91 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -63,6 +63,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; +import com.android.systemui.statusbar.phone.LightBarController; import java.util.function.Consumer; @@ -516,10 +517,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private final Drawable mBackground; private RemoteInputView mRemoteInputView; boolean mShowImeOnInputConnection; + private LightBarController mLightBarController; public RemoteEditText(Context context, AttributeSet attrs) { super(context, attrs); mBackground = getBackground(); + mLightBarController = Dependency.get(LightBarController.class); } private void defocusIfNeeded(boolean animate) { @@ -558,6 +561,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene if (!focused) { defocusIfNeeded(true /* animate */); } + if (!mRemoteInputView.mRemoved) { + mLightBarController.setDirectReplying(focused); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index 0f7a0f09b2e1..640f0f0cc3f6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -349,7 +349,7 @@ public class SmartReplyView extends ViewGroup { smartReplyController.smartActionClicked( entry, actionIndex, action, smartActions.fromAssistant); headsUpManager.removeNotification(entry.key, true); - }); + }, entry.getRow()); if (useDelayedOnClickListener) { onClickListener = new DelayedOnClickListener(onClickListener, smartReplyView.mConstants.getOnClickInitDelay()); diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 8380b19f7009..89aa7979e7d8 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -68,7 +68,8 @@ public class ThemeOverlayController extends SystemUI { mThemeManager = new ThemeOverlayManager( mContext.getSystemService(OverlayManager.class), AsyncTask.THREAD_POOL_EXECUTOR, - mContext.getString(R.string.launcher_overlayable_package)); + mContext.getString(R.string.launcher_overlayable_package), + mContext.getString(R.string.themepicker_overlayable_package)); final Handler bgHandler = Dependency.get(Dependency.BG_HANDLER); final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_SWITCHED); diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java index 27e3b2b438bf..930016be2d22 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java @@ -64,6 +64,9 @@ class ThemeOverlayManager { @VisibleForTesting static final String OVERLAY_CATEGORY_ICON_LAUNCHER = "android.theme.customization.icon_pack.launcher"; + @VisibleForTesting + static final String OVERLAY_CATEGORY_ICON_THEME_PICKER = + "android.theme.customization.icon_pack.themepicker"; /* * All theme customization categories used by the system, in order that they should be applied, @@ -76,7 +79,8 @@ class ThemeOverlayManager { OVERLAY_CATEGORY_COLOR, OVERLAY_CATEGORY_ICON_ANDROID, OVERLAY_CATEGORY_ICON_SYSUI, - OVERLAY_CATEGORY_ICON_SETTINGS); + OVERLAY_CATEGORY_ICON_SETTINGS, + OVERLAY_CATEGORY_ICON_THEME_PICKER); /* Categories that need to applied to the current user as well as the system user. */ @VisibleForTesting @@ -94,12 +98,14 @@ class ThemeOverlayManager { private final OverlayManager mOverlayManager; private final Executor mExecutor; private final String mLauncherPackage; + private final String mThemePickerPackage; ThemeOverlayManager(OverlayManager overlayManager, Executor executor, - String launcherPackage) { + String launcherPackage, String themePickerPackage) { mOverlayManager = overlayManager; mExecutor = executor; mLauncherPackage = launcherPackage; + mThemePickerPackage = themePickerPackage; mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet( OVERLAY_CATEGORY_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID)); @@ -109,6 +115,8 @@ class ThemeOverlayManager { Sets.newHashSet(OVERLAY_CATEGORY_ICON_SETTINGS)); mTargetPackageToCategories.put(mLauncherPackage, Sets.newHashSet(OVERLAY_CATEGORY_ICON_LAUNCHER)); + mTargetPackageToCategories.put(mThemePickerPackage, + Sets.newHashSet(OVERLAY_CATEGORY_ICON_THEME_PICKER)); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_COLOR, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_FONT, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_SHAPE, ANDROID_PACKAGE); @@ -116,6 +124,7 @@ class ThemeOverlayManager { mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_SYSUI, SYSUI_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_SETTINGS, SETTINGS_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_LAUNCHER, mLauncherPackage); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_THEME_PICKER, mThemePickerPackage); } /** diff --git a/packages/SystemUI/tests/res/values/overlayable_icons_test.xml b/packages/SystemUI/tests/res/values/overlayable_icons_test.xml index 5cc7976d3e60..4dc8d45a9d53 100644 --- a/packages/SystemUI/tests/res/values/overlayable_icons_test.xml +++ b/packages/SystemUI/tests/res/values/overlayable_icons_test.xml @@ -42,7 +42,6 @@ <item>@drawable/ic_power_low</item> <item>@drawable/ic_power_saver</item> <item>@drawable/ic_qs_bluetooth_connecting</item> - <item>@drawable/ic_qs_bluetooth_connecting</item> <item>@drawable/ic_qs_cancel</item> <item>@drawable/ic_qs_no_sim</item> <item>@drawable/ic_qs_wifi_0</item> diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java index 76f1684aae7a..3330d1e6d0a8 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java @@ -84,7 +84,6 @@ public final class ClockManagerTest extends SysuiTestCase { when(mMockInjectionInflationController.injectable(any())).thenReturn(inflater); mFakeDockManager = new DockManagerFake(); - getContext().putComponent(DockManager.class, mFakeDockManager); mCurrentUser = new MutableLiveData<>(); mCurrentUser.setValue(MAIN_USER_ID); @@ -92,7 +91,7 @@ public final class ClockManagerTest extends SysuiTestCase { mClockManager = new ClockManager(getContext(), mMockInjectionInflationController, mMockPluginManager, mMockColorExtractor, mMockContentResolver, - mMockCurrentUserObserable, mMockSettingsWrapper); + mMockCurrentUserObserable, mMockSettingsWrapper, mFakeDockManager); mClockManager.addOnClockChangedListener(mMockListener1); mClockManager.addOnClockChangedListener(mMockListener2); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java index df014a49e5cb..af2de1be1d57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java @@ -73,8 +73,6 @@ public class DozeDockHandlerTest extends SysuiTestCase { doReturn(false).when(mConfig).alwaysOnEnabled(anyInt()); mDockManagerFake = spy(new DockManagerFake()); - mContext.putComponent(DockManager.class, mDockManagerFake); - mDockHandler = new DozeDockHandler(mContext, mMachine, mHost, mConfig, Handler.createAsync(Looper.myLooper()), mDockManagerFake); } 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 1ac6bef1c7a5..6979fd811817 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -38,7 +38,6 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerFake; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.wakelock.WakeLock; @@ -84,7 +83,6 @@ public class DozeTriggersTest extends SysuiTestCase { mSensors = new FakeSensorManager(mContext); mWakeLock = new WakeLockFake(); mDockManagerFake = spy(new DockManagerFake()); - mContext.putComponent(DockManager.class, mDockManagerFake); mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, mConfig, mParameters, mSensors, Handler.createAsync(Looper.myLooper()), mWakeLock, true, diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java new file mode 100644 index 000000000000..16d665cbafcd --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.globalactions; + +import static junit.framework.Assert.assertEquals; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.testing.AndroidTestingRunner; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.leak.RotationUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link ListGridLayout}. + */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class GlobalActionsColumnLayoutTest extends SysuiTestCase { + + private GlobalActionsColumnLayout mColumnLayout; + + @Before + public void setUp() throws Exception { + mColumnLayout = spy((GlobalActionsColumnLayout) + LayoutInflater.from(mContext).inflate(R.layout.global_actions_column, null)); + } + + @Test + public void testShouldReverseListItems() { + doReturn(View.LAYOUT_DIRECTION_LTR).when(mColumnLayout).getCurrentLayoutDirection(); + + doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation(); + assertEquals(false, mColumnLayout.shouldReverseListItems()); + + doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation(); + assertEquals(false, mColumnLayout.shouldReverseListItems()); + + doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation(); + assertEquals(true, mColumnLayout.shouldReverseListItems()); + + doReturn(View.LAYOUT_DIRECTION_RTL).when(mColumnLayout).getCurrentLayoutDirection(); + + doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation(); + assertEquals(true, mColumnLayout.shouldReverseListItems()); + + doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation(); + assertEquals(false, mColumnLayout.shouldReverseListItems()); + + doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation(); + assertEquals(false, mColumnLayout.shouldReverseListItems()); + } + + @Test + public void testGetAnimationOffsetX() { + doReturn(50f).when(mColumnLayout).getAnimationDistance(); + + doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation(); + assertEquals(50f, mColumnLayout.getAnimationOffsetX(), .01); + + doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation(); + assertEquals(0, mColumnLayout.getAnimationOffsetX(), .01); + + doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation(); + assertEquals(0, mColumnLayout.getAnimationOffsetX(), .01); + } + + @Test + public void testGetAnimationOffsetY() { + doReturn(50f).when(mColumnLayout).getAnimationDistance(); + + doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation(); + assertEquals(0, mColumnLayout.getAnimationOffsetY(), .01); + + doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation(); + assertEquals(-50f, mColumnLayout.getAnimationOffsetY(), .01); + + doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation(); + assertEquals(50f, mColumnLayout.getAnimationOffsetY(), .01); + } + + @Test + public void testSnapToPowerButton_portrait() { + doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation(); + doReturn(50).when(mColumnLayout).getPowerButtonOffsetDistance(); + + mColumnLayout.snapToPowerButton(); + assertEquals(Gravity.TOP | Gravity.RIGHT, mColumnLayout.getGravity()); + assertEquals(50, mColumnLayout.getPaddingTop(), .01); + } + + @Test + public void testCenterAlongEdge_portrait() { + doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation(); + + mColumnLayout.centerAlongEdge(); + assertEquals(Gravity.CENTER_VERTICAL | Gravity.RIGHT, mColumnLayout.getGravity()); + assertEquals(0, mColumnLayout.getPaddingTop(), .01); + } + + @Test + public void testUpdateSnap_initialState() { + doReturn(false).when(mColumnLayout).shouldSnapToPowerButton(); + + mColumnLayout.updateSnap(); // should do nothing, since snap has not changed from init state + + verify(mColumnLayout, times(0)).snapToPowerButton(); + verify(mColumnLayout, times(0)).centerAlongEdge(); + } + + @Test + public void testUpdateSnap_snapThenSnap() { + doReturn(true).when(mColumnLayout).shouldSnapToPowerButton(); + + mColumnLayout.updateSnap(); // should snap to power button + + verify(mColumnLayout, times(1)).snapToPowerButton(); + verify(mColumnLayout, times(0)).centerAlongEdge(); + + mColumnLayout.updateSnap(); // should do nothing, since this is the same state as last time + + verify(mColumnLayout, times(1)).snapToPowerButton(); + verify(mColumnLayout, times(0)).centerAlongEdge(); + } + + @Test + public void testUpdateSnap_snapThenCenter() { + doReturn(true).when(mColumnLayout).shouldSnapToPowerButton(); + + mColumnLayout.updateSnap(); // should snap to power button + + verify(mColumnLayout, times(1)).snapToPowerButton(); + verify(mColumnLayout, times(0)).centerAlongEdge(); + + doReturn(false).when(mColumnLayout).shouldSnapToPowerButton(); + + mColumnLayout.updateSnap(); // should center to edge + + verify(mColumnLayout, times(1)).snapToPowerButton(); + verify(mColumnLayout, times(1)).centerAlongEdge(); + } + + @Test + public void testShouldSnapToPowerButton_vertical() { + doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation(); + doReturn(300).when(mColumnLayout).getPowerButtonOffsetDistance(); + doReturn(1000).when(mColumnLayout).getMeasuredHeight(); + View wrapper = spy(new View(mContext, null)); + doReturn(wrapper).when(mColumnLayout).getWrapper(); + doReturn(500).when(wrapper).getMeasuredHeight(); + + assertEquals(true, mColumnLayout.shouldSnapToPowerButton()); + + doReturn(600).when(mColumnLayout).getMeasuredHeight(); + + assertEquals(false, mColumnLayout.shouldSnapToPowerButton()); + } + + @Test + public void testShouldSnapToPowerButton_horizontal() { + doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation(); + doReturn(300).when(mColumnLayout).getPowerButtonOffsetDistance(); + doReturn(1000).when(mColumnLayout).getMeasuredWidth(); + View wrapper = spy(new View(mContext, null)); + doReturn(wrapper).when(mColumnLayout).getWrapper(); + doReturn(500).when(wrapper).getMeasuredWidth(); + + assertEquals(true, mColumnLayout.shouldSnapToPowerButton()); + + doReturn(600).when(mColumnLayout).getMeasuredWidth(); + + assertEquals(false, mColumnLayout.shouldSnapToPowerButton()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java index 3c52e9d86c15..a396f3e8bf46 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java @@ -18,12 +18,8 @@ package com.android.systemui.globalactions; import static junit.framework.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import android.testing.AndroidTestingRunner; import android.view.LayoutInflater; @@ -32,7 +28,6 @@ import android.view.ViewGroup; import androidx.test.filters.SmallTest; -import com.android.systemui.MultiListLayout; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.util.leak.RotationUtils; @@ -49,61 +44,12 @@ import org.junit.runner.RunWith; public class GlobalActionsGridLayoutTest extends SysuiTestCase { private GlobalActionsGridLayout mGridLayout; - private TestAdapter mAdapter; private ListGridLayout mListGrid; - private class TestAdapter extends MultiListLayout.MultiListAdapter { - @Override - public void onClickItem(int index) { } - - @Override - public boolean onLongClickItem(int index) { - return true; - } - - @Override - public int countSeparatedItems() { - return -1; - } - - @Override - public int countListItems() { - return -1; - } - - @Override - public boolean shouldBeSeparated(int position) { - return false; - } - - @Override - public int getCount() { - return countSeparatedItems() + countListItems(); - } - - @Override - public Object getItem(int position) { - return null; - } - - @Override - public long getItemId(int position) { - return -1; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - return null; - } - } - - @Before public void setUp() throws Exception { mGridLayout = spy((GlobalActionsGridLayout) LayoutInflater.from(mContext).inflate(R.layout.global_actions_grid, null)); - mAdapter = spy(new TestAdapter()); - mGridLayout.setAdapter(mAdapter); mListGrid = spy(mGridLayout.getListView()); doReturn(mListGrid).when(mGridLayout).getListView(); } @@ -122,7 +68,7 @@ public class GlobalActionsGridLayoutTest extends SysuiTestCase { @Test public void testShouldReverseListItems() { - doReturn(View.LAYOUT_DIRECTION_LTR).when(mGridLayout).getLayoutDirection(); + doReturn(View.LAYOUT_DIRECTION_LTR).when(mGridLayout).getCurrentLayoutDirection(); doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mGridLayout).getCurrentRotation(); assertEquals(false, mGridLayout.shouldReverseListItems()); @@ -133,7 +79,7 @@ public class GlobalActionsGridLayoutTest extends SysuiTestCase { doReturn(RotationUtils.ROTATION_SEASCAPE).when(mGridLayout).getCurrentRotation(); assertEquals(true, mGridLayout.shouldReverseListItems()); - doReturn(View.LAYOUT_DIRECTION_RTL).when(mGridLayout).getLayoutDirection(); + doReturn(View.LAYOUT_DIRECTION_RTL).when(mGridLayout).getCurrentLayoutDirection(); doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mGridLayout).getCurrentRotation(); assertEquals(true, mGridLayout.shouldReverseListItems()); @@ -185,123 +131,26 @@ public class GlobalActionsGridLayoutTest extends SysuiTestCase { assertEquals(0f, mGridLayout.getAnimationOffsetY(), .01); } - @Test(expected = IllegalStateException.class) - public void testOnUpdateList_noAdapter() { - mGridLayout.setAdapter(null); - mGridLayout.updateList(); - } - @Test - public void testOnUpdateList_noItems() { - doReturn(0).when(mAdapter).countSeparatedItems(); - doReturn(0).when(mAdapter).countListItems(); - mGridLayout.updateList(); - - ViewGroup separatedView = mGridLayout.getSeparatedView(); - ListGridLayout listView = mGridLayout.getListView(); - - assertEquals(0, separatedView.getChildCount()); - assertEquals(View.GONE, separatedView.getVisibility()); - - verify(mListGrid, times(0)).addItem(any()); - } - - @Test - public void testOnUpdateList_resizesFirstSeparatedItem() { - doReturn(1).when(mAdapter).countSeparatedItems(); - doReturn(0).when(mAdapter).countListItems(); + public void testUpdateSeparatedItemSize() { View firstView = new View(mContext, null); View secondView = new View(mContext, null); - doReturn(firstView).when(mAdapter).getView(eq(0), any(), any()); - doReturn(true).when(mAdapter).shouldBeSeparated(0); + ViewGroup separatedView = mGridLayout.getSeparatedView(); + separatedView.addView(firstView); - mGridLayout.updateList(); + mGridLayout.updateSeparatedItemSize(); ViewGroup.LayoutParams childParams = firstView.getLayoutParams(); assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, childParams.width); assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, childParams.height); - doReturn(2).when(mAdapter).countSeparatedItems(); - doReturn(secondView).when(mAdapter).getView(eq(1), any(), any()); - doReturn(true).when(mAdapter).shouldBeSeparated(1); + separatedView.addView(secondView); - mGridLayout.updateList(); + mGridLayout.updateSeparatedItemSize(); childParams = firstView.getLayoutParams(); assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, childParams.width); assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, childParams.height); - - - } - - @Test - public void testOnUpdateList_onlySeparatedItems() { - doReturn(1).when(mAdapter).countSeparatedItems(); - doReturn(0).when(mAdapter).countListItems(); - View testView = new View(mContext, null); - doReturn(testView).when(mAdapter).getView(eq(0), any(), any()); - doReturn(true).when(mAdapter).shouldBeSeparated(0); - - mGridLayout.updateList(); - - verify(mListGrid, times(0)).addItem(any()); - } - - @Test - public void testOnUpdateList_oneSeparatedOneList() { - doReturn(1).when(mAdapter).countSeparatedItems(); - doReturn(1).when(mAdapter).countListItems(); - View view1 = new View(mContext, null); - View view2 = new View(mContext, null); - - doReturn(view1).when(mAdapter).getView(eq(0), any(), any()); - doReturn(true).when(mAdapter).shouldBeSeparated(0); - - doReturn(view2).when(mAdapter).getView(eq(1), any(), any()); - doReturn(false).when(mAdapter).shouldBeSeparated(1); - - mGridLayout.updateList(); - - ViewGroup separatedView = mGridLayout.getSeparatedView(); - - assertEquals(1, separatedView.getChildCount()); - assertEquals(View.VISIBLE, separatedView.getVisibility()); - assertEquals(view1, separatedView.getChildAt(0)); - - verify(mListGrid, times(1)).addItem(view2); - } - - @Test - public void testOnUpdateList_fourInList() { - doReturn(0).when(mAdapter).countSeparatedItems(); - doReturn(4).when(mAdapter).countListItems(); - View view1 = new View(mContext, null); - View view2 = new View(mContext, null); - View view3 = new View(mContext, null); - View view4 = new View(mContext, null); - - doReturn(view1).when(mAdapter).getView(eq(0), any(), any()); - doReturn(false).when(mAdapter).shouldBeSeparated(0); - - doReturn(view2).when(mAdapter).getView(eq(1), any(), any()); - doReturn(false).when(mAdapter).shouldBeSeparated(1); - - doReturn(view3).when(mAdapter).getView(eq(2), any(), any()); - doReturn(false).when(mAdapter).shouldBeSeparated(2); - - doReturn(view4).when(mAdapter).getView(eq(3), any(), any()); - doReturn(false).when(mAdapter).shouldBeSeparated(3); - - mGridLayout.updateList(); - - ViewGroup separatedView = mGridLayout.getSeparatedView(); - assertEquals(0, separatedView.getChildCount()); - assertEquals(View.GONE, separatedView.getVisibility()); - - verify(mListGrid, times(1)).addItem(view1); - verify(mListGrid, times(1)).addItem(view2); - verify(mListGrid, times(1)).addItem(view3); - verify(mListGrid, times(1)).addItem(view4); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java new file mode 100644 index 000000000000..16dcd659b3f9 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.globalactions; + +import static junit.framework.Assert.assertEquals; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import android.content.Context; +import android.testing.AndroidTestingRunner; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.MultiListLayout; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; + +/** + * Tests for {@link ListGridLayout}. + */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class GlobalActionsLayoutTest extends SysuiTestCase { + + private TestLayout mLayout; + private TestAdapter mAdapter; + + private class TestAdapter extends MultiListLayout.MultiListAdapter { + @Override + public void onClickItem(int index) { } + + @Override + public boolean onLongClickItem(int index) { + return true; + } + + @Override + public int countSeparatedItems() { + return -1; + } + + @Override + public int countListItems() { + return -1; + } + + @Override + public boolean shouldBeSeparated(int position) { + return false; + } + + @Override + public int getCount() { + return countSeparatedItems() + countListItems(); + } + + @Override + public Object getItem(int position) { + return null; + } + + @Override + public long getItemId(int position) { + return -1; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + return null; + } + } + + private class TestLayout extends GlobalActionsLayout { + ArrayList<View> mSeparatedViews = new ArrayList<>(); + ArrayList<View> mListViews = new ArrayList<>(); + boolean mSeparatedViewVisible = false; + + TestLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected boolean shouldReverseListItems() { + return false; + } + + @Override + public float getAnimationOffsetX() { + return 0; + } + + @Override + public float getAnimationOffsetY() { + return 0; + } + + @Override + protected void addToListView(View v, boolean reverse) { + if (reverse) { + mListViews.add(0, v); + } else { + mListViews.add(v); + } + } + + @Override + protected void addToSeparatedView(View v, boolean reverse) { + if (reverse) { + mSeparatedViews.add(0, v); + } else { + mSeparatedViews.add(v); + } + } + + @Override + protected void setSeparatedViewVisibility(boolean visible) { + mSeparatedViewVisible = visible; + } + } + + @Before + public void setUp() throws Exception { + mLayout = spy(new TestLayout(mContext, null)); + mAdapter = spy(new TestAdapter()); + mLayout.setAdapter(mAdapter); + } + + @Test(expected = IllegalStateException.class) + public void testOnUpdateList_noAdapter() { + mLayout.setAdapter(null); + mLayout.updateList(); + } + + @Test + public void testOnUpdateList_noItems() { + doReturn(0).when(mAdapter).countSeparatedItems(); + doReturn(0).when(mAdapter).countListItems(); + mLayout.updateList(); + + assertEquals(0, mLayout.mSeparatedViews.size()); + assertEquals(0, mLayout.mListViews.size()); + + assertEquals(false, mLayout.mSeparatedViewVisible); + } + + @Test + public void testOnUpdateList_oneSeparatedOneList() { + doReturn(1).when(mAdapter).countSeparatedItems(); + doReturn(1).when(mAdapter).countListItems(); + View view1 = new View(mContext, null); + View view2 = new View(mContext, null); + + doReturn(view1).when(mAdapter).getView(eq(0), any(), any()); + doReturn(true).when(mAdapter).shouldBeSeparated(0); + + doReturn(view2).when(mAdapter).getView(eq(1), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(1); + + mLayout.updateList(); + + assertEquals(1, mLayout.mSeparatedViews.size()); + assertEquals(1, mLayout.mListViews.size()); + assertEquals(view1, mLayout.mSeparatedViews.get(0)); + assertEquals(view2, mLayout.mListViews.get(0)); + } + + + @Test + public void testOnUpdateList_twoSeparatedItems() { + doReturn(2).when(mAdapter).countSeparatedItems(); + doReturn(0).when(mAdapter).countListItems(); + View view1 = new View(mContext, null); + View view2 = new View(mContext, null); + + doReturn(view1).when(mAdapter).getView(eq(0), any(), any()); + doReturn(true).when(mAdapter).shouldBeSeparated(0); + doReturn(view2).when(mAdapter).getView(eq(1), any(), any()); + doReturn(true).when(mAdapter).shouldBeSeparated(1); + + mLayout.updateList(); + + assertEquals(2, mLayout.mSeparatedViews.size()); + assertEquals(0, mLayout.mListViews.size()); + + assertEquals(view1, mLayout.mSeparatedViews.get(0)); + assertEquals(view2, mLayout.mSeparatedViews.get(1)); + + // if separated view has items in it, should be made visible + assertEquals(true, mLayout.mSeparatedViewVisible); + } + + @Test + public void testOnUpdateList_twoSeparatedItems_reverse() { + doReturn(2).when(mAdapter).countSeparatedItems(); + doReturn(0).when(mAdapter).countListItems(); + doReturn(true).when(mLayout).shouldReverseListItems(); + View view1 = new View(mContext, null); + View view2 = new View(mContext, null); + + doReturn(view1).when(mAdapter).getView(eq(0), any(), any()); + doReturn(true).when(mAdapter).shouldBeSeparated(0); + + doReturn(view2).when(mAdapter).getView(eq(1), any(), any()); + doReturn(true).when(mAdapter).shouldBeSeparated(1); + + mLayout.updateList(); + + assertEquals(2, mLayout.mSeparatedViews.size()); + assertEquals(0, mLayout.mListViews.size()); + + // separated view items are not reversed in current implementation, and this is intentional! + assertEquals(view1, mLayout.mSeparatedViews.get(0)); + assertEquals(view2, mLayout.mSeparatedViews.get(1)); + } + + @Test + public void testOnUpdateList_fourInList() { + doReturn(0).when(mAdapter).countSeparatedItems(); + doReturn(4).when(mAdapter).countListItems(); + View view1 = new View(mContext, null); + View view2 = new View(mContext, null); + View view3 = new View(mContext, null); + View view4 = new View(mContext, null); + + doReturn(view1).when(mAdapter).getView(eq(0), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(0); + + doReturn(view2).when(mAdapter).getView(eq(1), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(1); + + doReturn(view3).when(mAdapter).getView(eq(2), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(2); + + doReturn(view4).when(mAdapter).getView(eq(3), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(3); + + mLayout.updateList(); + + assertEquals(0, mLayout.mSeparatedViews.size()); + assertEquals(4, mLayout.mListViews.size()); + assertEquals(view1, mLayout.mListViews.get(0)); + assertEquals(view2, mLayout.mListViews.get(1)); + assertEquals(view3, mLayout.mListViews.get(2)); + assertEquals(view4, mLayout.mListViews.get(3)); + } + + @Test + public void testOnUpdateList_fourInList_reverse() { + doReturn(0).when(mAdapter).countSeparatedItems(); + doReturn(4).when(mAdapter).countListItems(); + doReturn(true).when(mLayout).shouldReverseListItems(); + View view1 = new View(mContext, null); + View view2 = new View(mContext, null); + View view3 = new View(mContext, null); + View view4 = new View(mContext, null); + + doReturn(view1).when(mAdapter).getView(eq(0), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(0); + + doReturn(view2).when(mAdapter).getView(eq(1), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(1); + + doReturn(view3).when(mAdapter).getView(eq(2), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(2); + + doReturn(view4).when(mAdapter).getView(eq(3), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(3); + + mLayout.updateList(); + + assertEquals(0, mLayout.mSeparatedViews.size()); + assertEquals(4, mLayout.mListViews.size()); + assertEquals(view1, mLayout.mListViews.get(3)); + assertEquals(view2, mLayout.mListViews.get(2)); + assertEquals(view3, mLayout.mListViews.get(1)); + assertEquals(view4, mLayout.mListViews.get(0)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index c2f55e2f9b99..b049632942cf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -112,33 +112,19 @@ public class CommandQueueTest extends SysuiTestCase { @Test public void testSetSystemUiVisibility() { Rect r = new Rect(); - mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r); + mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r, false); waitForIdleSync(); verify(mCallbacks).setSystemUiVisibility(eq(DEFAULT_DISPLAY), eq(1), eq(2), eq(3), eq(4), - eq(null), eq(r)); + eq(null), eq(r), eq(false)); } @Test public void testSetSystemUiVisibilityForSecondaryDisplay() { Rect r = new Rect(); - mCommandQueue.setSystemUiVisibility(SECONDARY_DISPLAY, 1, 2, 3, 4, null, r); + mCommandQueue.setSystemUiVisibility(SECONDARY_DISPLAY, 1, 2, 3, 4, null, r, false); waitForIdleSync(); verify(mCallbacks).setSystemUiVisibility(eq(SECONDARY_DISPLAY), eq(1), eq(2), eq(3), eq(4), - eq(null), eq(r)); - } - - @Test - public void testTopAppWindowChanged() { - mCommandQueue.topAppWindowChanged(DEFAULT_DISPLAY, true); - waitForIdleSync(); - verify(mCallbacks).topAppWindowChanged(eq(DEFAULT_DISPLAY), eq(true)); - } - - @Test - public void testTopAppWindowChangedForSecondaryDisplay() { - mCommandQueue.topAppWindowChanged(SECONDARY_DISPLAY, true); - waitForIdleSync(); - verify(mCallbacks).topAppWindowChanged(eq(SECONDARY_DISPLAY), eq(true)); + eq(null), eq(r), eq(false)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java index b03abecbc59b..7eeae67c9fdf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertNotSame; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -24,17 +26,22 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import android.annotation.Nullable; import android.app.Notification; import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.drawable.Drawable; import android.test.suitebuilder.annotation.SmallTest; import android.widget.RemoteViews; +import androidx.palette.graphics.Palette; import androidx.test.runner.AndroidJUnit4; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,9 +50,18 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class MediaNotificationProcessorTest extends SysuiTestCase { + private static final int BITMAP_WIDTH = 10; + private static final int BITMAP_HEIGHT = 10; + + /** + * Color tolerance is borrowed from the AndroidX test utilities for Palette. + */ + private static final int COLOR_TOLERANCE = 8; + private MediaNotificationProcessor mProcessor; private Bitmap mBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); private ImageGradientColorizer mColorizer; + @Nullable private Bitmap mArtwork; @Before public void setUp() { @@ -53,6 +69,14 @@ public class MediaNotificationProcessorTest extends SysuiTestCase { mProcessor = new MediaNotificationProcessor(getContext(), getContext(), mColorizer); } + @After + public void tearDown() { + if (mArtwork != null) { + mArtwork.recycle(); + mArtwork = null; + } + } + @Test public void testColorizedWithLargeIcon() { Notification.Builder builder = new Notification.Builder(getContext()).setSmallIcon( @@ -100,6 +124,36 @@ public class MediaNotificationProcessorTest extends SysuiTestCase { assertNotSame(contentView, remoteViews); } + @Test + public void findBackgroundSwatch_white() { + // Given artwork that is completely white. + mArtwork = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(mArtwork); + canvas.drawColor(Color.WHITE); + // WHEN the background swatch is computed + Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(mArtwork); + // THEN the swatch color is white + assertCloseColors(swatch.getRgb(), Color.WHITE); + } + + @Test + public void findBackgroundSwatch_red() { + // Given artwork that is completely red. + mArtwork = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(mArtwork); + canvas.drawColor(Color.RED); + // WHEN the background swatch is computed + Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(mArtwork); + // THEN the swatch color is red + assertCloseColors(swatch.getRgb(), Color.RED); + } + + static void assertCloseColors(int expected, int actual) { + assertThat((float) Color.red(expected)).isWithin(COLOR_TOLERANCE).of(Color.red(actual)); + assertThat((float) Color.green(expected)).isWithin(COLOR_TOLERANCE).of(Color.green(actual)); + assertThat((float) Color.blue(expected)).isWithin(COLOR_TOLERANCE).of(Color.blue(actual)); + } + public static class TestableColorizer extends ImageGradientColorizer { private final Bitmap mBitmap; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java index 1b34a7584994..f614354a7691 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java @@ -70,7 +70,7 @@ public class AutoHideControllerTest extends SysuiTestCase { @Test public void testSetSystemUiVisibilityEarlyReturnWithDifferentDisplay() { - mAutoHideController.setSystemUiVisibility(1, 1, 2, 3, 4, null, new Rect()); + mAutoHideController.setSystemUiVisibility(1, 1, 2, 3, 4, null, new Rect(), false); verify(mAutoHideController, never()).notifySystemUiVisibilityChanged(anyInt()); } @@ -78,7 +78,8 @@ public class AutoHideControllerTest extends SysuiTestCase { @Test public void testSetSystemUiVisibilityEarlyReturnWithSameVisibility() { mAutoHideController - .setSystemUiVisibility(DEFAULT_DISPLAY, View.VISIBLE, 2, 3, 4, null, new Rect()); + .setSystemUiVisibility( + DEFAULT_DISPLAY, View.VISIBLE, 2, 3, 4, null, new Rect(), false); verify(mAutoHideController, never()).notifySystemUiVisibilityChanged(anyInt()); } @@ -92,7 +93,7 @@ public class AutoHideControllerTest extends SysuiTestCase { View.SYSTEM_UI_FLAG_FULLSCREEN | View.STATUS_BAR_UNHIDE; mAutoHideController.setSystemUiVisibility( - DEFAULT_DISPLAY, expectedStatus, 2, 3, FULL_MASK, null, new Rect()); + DEFAULT_DISPLAY, expectedStatus, 2, 3, FULL_MASK, null, new Rect(), false); assertEquals("System UI visibility should not be changed", expectedStatus, mAutoHideController.mSystemUiVisibility); @@ -109,7 +110,7 @@ public class AutoHideControllerTest extends SysuiTestCase { mAutoHideController.setSystemUiVisibility( DEFAULT_DISPLAY, View.STATUS_BAR_UNHIDE | View.NAVIGATION_BAR_UNHIDE, - 2, 3, FULL_MASK, null, new Rect()); + 2, 3, FULL_MASK, null, new Rect(), false); int expectedStatus = View.VISIBLE; assertEquals(expectedStatus, mAutoHideController.mSystemUiVisibility); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 1c6e3b09df44..f50cf5a70cdc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.phone; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -196,6 +197,17 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { verify(mBouncer, never()).setExpansion(anyFloat()); } + @Test + public void setOccluded_animatesPanelExpansion_onlyIfBouncerHidden() { + mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */); + verify(mStatusBar).animateKeyguardUnoccluding(); + + when(mBouncer.isShowing()).thenReturn(true); + clearInvocations(mStatusBar); + mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */); + verify(mStatusBar, never()).animateKeyguardUnoccluding(); + } + private class TestableStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager { public TestableStatusBarKeyguardViewManager(Context context, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java index a88a59540161..a97832f94924 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java @@ -70,7 +70,8 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase { mNotificationLockscreenUserManager); mDependency.putComponent(CommandQueue.class, mock(CommandQueue.class)); - mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext)); + mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext, + mock(NotificationGroupManager.class))); mRemoteInputCallback.mChallengeReceiver = mRemoteInputCallback.new ChallengeReceiver(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java index 8bf1606d5b7b..bc468bf2fb82 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java @@ -42,6 +42,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationTestHelper; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.util.Assert; import org.junit.After; @@ -67,6 +68,7 @@ public class RemoteInputViewTest extends SysuiTestCase { @Mock private RemoteInputController mController; @Mock private ShortcutManager mShortcutManager; @Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; + @Mock private LightBarController mLightBarController; private BlockingQueueIntentReceiver mReceiver; private RemoteInputView mView; @@ -77,6 +79,8 @@ public class RemoteInputViewTest extends SysuiTestCase { mDependency.injectTestDependency(RemoteInputQuickSettingsDisabler.class, mRemoteInputQuickSettingsDisabler); + mDependency.injectTestDependency(LightBarController.class, + mLightBarController); mReceiver = new BlockingQueueIntentReceiver(); mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION), null, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java index 01f3c923832f..8c5fac47885f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java @@ -634,7 +634,8 @@ public class SmartReplyViewTest extends SysuiTestCase { mView.getChildAt(2).performClick(); - verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any()); + verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(), + any()); } @Test @@ -645,7 +646,7 @@ public class SmartReplyViewTest extends SysuiTestCase { mView.getChildAt(2).performClick(); - verify(mActivityStarter, never()).startPendingIntentDismissingKeyguard(any(), any()); + verify(mActivityStarter, never()).startPendingIntentDismissingKeyguard(any(), any(), any()); } @Test @@ -657,7 +658,8 @@ public class SmartReplyViewTest extends SysuiTestCase { Thread.sleep(delayMs); mView.getChildAt(2).performClick(); - verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any()); + verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(), + any()); } @Test @@ -668,7 +670,8 @@ public class SmartReplyViewTest extends SysuiTestCase { mView.getChildAt(2).performClick(); - verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any()); + verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(), + any()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java index 4659afc8e1c2..c99deb606459 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java @@ -22,6 +22,7 @@ import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_IC import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_LAUNCHER; import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_SETTINGS; import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_SYSUI; +import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_THEME_PICKER; import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_SHAPE; import static com.android.systemui.theme.ThemeOverlayManager.SETTINGS_PACKAGE; import static com.android.systemui.theme.ThemeOverlayManager.SYSTEM_USER_CATEGORIES; @@ -74,6 +75,7 @@ public class ThemeOverlayManagerTest extends SysuiTestCase { } } + private static final String THEMEPICKER_PACKAGE = "com.android.wallpaper"; private static final String LAUNCHER_PACKAGE = "com.android.launcher3"; private static final UserHandle TEST_USER = UserHandle.of(5); private static final Set<UserHandle> TEST_USER_HANDLES = Sets.newHashSet(TEST_USER); @@ -87,7 +89,7 @@ public class ThemeOverlayManagerTest extends SysuiTestCase { public void setup() throws Exception { MockitoAnnotations.initMocks(this); mManager = new ThemeOverlayManager(mOverlayManager, MoreExecutors.directExecutor(), - LAUNCHER_PACKAGE); + LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE); when(mOverlayManager.getOverlayInfosForTarget(ANDROID_PACKAGE, UserHandle.SYSTEM)) .thenReturn(Lists.newArrayList( createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_COLOR, @@ -124,6 +126,12 @@ public class ThemeOverlayManagerTest extends SysuiTestCase { LAUNCHER_PACKAGE, OVERLAY_CATEGORY_ICON_LAUNCHER, false), createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_LAUNCHER, LAUNCHER_PACKAGE, OVERLAY_CATEGORY_ICON_LAUNCHER, true))); + when(mOverlayManager.getOverlayInfosForTarget(THEMEPICKER_PACKAGE, UserHandle.SYSTEM)) + .thenReturn(Lists.newArrayList( + createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ICON_THEME_PICKER, + THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, false), + createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_THEME_PICKER, + THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, true))); } @Test @@ -222,6 +230,8 @@ public class ThemeOverlayManagerTest extends SysuiTestCase { verify(mOverlayManager, never()).getOverlayInfosForTarget(SYSUI_PACKAGE, UserHandle.SYSTEM); verify(mOverlayManager, never()).getOverlayInfosForTarget(LAUNCHER_PACKAGE, UserHandle.SYSTEM); + verify(mOverlayManager, never()).getOverlayInfosForTarget(THEMEPICKER_PACKAGE, + UserHandle.SYSTEM); } private static OverlayInfo createOverlayInfo(String packageName, String targetPackageName, diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk index b9b3a615650f..2675e900fd6d 100644 --- a/packages/overlays/Android.mk +++ b/packages/overlays/Android.mk @@ -32,14 +32,17 @@ LOCAL_REQUIRED_MODULES := \ IconPackCircularLauncherOverlay \ IconPackCircularSettingsOverlay \ IconPackCircularSystemUIOverlay \ + IconPackCircularThemePickerOverlay \ IconPackFilledAndroidOverlay \ IconPackFilledLauncherOverlay \ IconPackFilledSettingsOverlay \ IconPackFilledSystemUIOverlay \ + IconPackFilledThemePickerOverlay \ IconPackRoundedAndroidOverlay \ IconPackRoundedLauncherOverlay \ IconPackRoundedSettingsOverlay \ IconPackRoundedSystemUIOverlay \ + IconPackRoundedThemePickerUIOverlay \ IconShapeRoundedRectOverlay \ IconShapeSquareOverlay \ IconShapeSquircleOverlay \ diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_0.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_0.xml new file mode 100644 index 000000000000..1be546840e6d --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_0.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="150" + android:startOffset="0" android:valueFrom="0.3" + android:valueTo="0.3" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="150" android:valueFrom="0.3" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="483" + android:startOffset="167" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="650" android:valueFrom="1" + android:valueTo="0.3" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> +</set> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_1.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_1.xml new file mode 100644 index 000000000000..c9fd424f7ad3 --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_1.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="317" + android:startOffset="0" android:valueFrom="0.3" + android:valueTo="0.3" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="317" android:valueFrom="0.3" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="483" + android:startOffset="333" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="817" android:valueFrom="1" + android:valueTo="0.3" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> +</set> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_2.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_2.xml new file mode 100644 index 000000000000..b34d3088e87e --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_2.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="483" + android:startOffset="0" android:valueFrom="0.3" + android:valueTo="0.3" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="483" android:valueFrom="0.3" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="483" + android:startOffset="500" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="983" android:valueFrom="1" + android:valueTo="0.3" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> +</set> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_3.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_3.xml new file mode 100644 index 000000000000..9d2b3a4f8016 --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_3.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="650" + android:startOffset="0" android:valueFrom="0.3" + android:valueTo="0.3" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="650" android:valueFrom="0.3" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="483" + android:startOffset="667" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="1150" android:valueFrom="1" + android:valueTo="0.3" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> +</set> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_4.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_4.xml new file mode 100644 index 000000000000..943893dfb361 --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_4.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="translateX" android:duration="1250" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml new file mode 100644 index 000000000000..8ca452097c4d --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_bluetooth_transient_animation_drawable"> + <target android:name="_R_G_L_1_G_D_0_P_0" + android:animation="@*android:anim/ic_bluetooth_transient_animation_0"/> + <target android:name="_R_G_L_0_G_D_0_P_0" + android:animation="@*android:anim/ic_bluetooth_transient_animation_1"/> + <target android:name="time_group" + android:animation="@*android:anim/ic_bluetooth_transient_animation_2"/> +</animated-vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml new file mode 100644 index 000000000000..66ac8feff21b --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" + android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_2_G" android:translateX="3.75" + android:translateY="1.75"> + <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M1.22 3.22 C0.93,3.51 0.93,3.99 1.22,4.28 C1.22,4.28 7.19,10.25 7.19,10.25 C7.19,10.25 1.22,16.22 1.22,16.22 C0.93,16.51 0.93,16.99 1.22,17.28 C1.37,17.43 1.56,17.5 1.75,17.5 C1.94,17.5 2.13,17.43 2.28,17.28 C2.28,17.28 7.25,12.31 7.25,12.31 C7.25,12.31 7.25,20.25 7.25,20.25 C7.25,20.25 8,20.25 8,20.25 C10.96,20.25 13.37,17.84 13.37,14.88 C13.37,12.91 12.31,11.19 10.73,10.25 C12.31,9.31 13.37,7.59 13.37,5.63 C13.37,2.66 10.96,0.25 8,0.25 C8,0.25 7.25,0.25 7.25,0.25 C7.25,0.25 7.25,8.19 7.25,8.19 C7.25,8.19 2.28,3.22 2.28,3.22 C1.99,2.93 1.51,2.93 1.22,3.22c M8.75 1.82 C10.52,2.17 11.87,3.75 11.87,5.63 C11.87,7.5 10.52,9.08 8.75,9.43 C8.75,9.43 8.75,1.82 8.75,1.82c M8.75 11.07 C10.52,11.42 11.87,13 11.87,14.88 C11.87,16.75 10.52,18.33 8.75,18.68 C8.75,18.68 8.75,11.07 8.75,11.07c "/> + </group> + <group android:name="_R_G_L_1_G" android:translateX="3.75" + android:translateY="1.75"> + <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M15.25 9.25 C14.7,9.25 14.25,9.7 14.25,10.25 C14.25,10.8 14.7,11.25 15.25,11.25 C15.8,11.25 16.25,10.8 16.25,10.25 C16.25,9.7 15.8,9.25 15.25,9.25c "/> + </group> + <group android:name="_R_G_L_0_G" android:translateX="3.75" + android:translateY="1.75"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M0.25 10.25 C0.25,10.8 0.7,11.25 1.25,11.25 C1.8,11.25 2.25,10.8 2.25,10.25 C2.25,9.7 1.8,9.25 1.25,9.25 C0.7,9.25 0.25,9.7 0.25,10.25c "/> + </group> + </group> + <group android:name="time_group"/> +</vector> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml new file mode 100644 index 000000000000..1317f66b4131 --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_hotspot_transient_animation_drawable"> + <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_0"/> + <target android:name="_R_G_L_0_G_D_1_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_1"/> + <target android:name="_R_G_L_0_G_D_2_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_2"/> + <target android:name="time_group" android:animation="@*android:anim/ic_hotspot_transient_animation_3"/> +</animated-vector> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml new file mode 100644 index 000000000000..81531957f24d --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_0_G" android:translateX="2" + android:translateY="1.552999999999999"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M10 12.45 C9.17,12.45 8.5,11.77 8.5,10.95 C8.5,10.12 9.17,9.45 10,9.45 C10.83,9.45 11.5,10.12 11.5,10.95 C11.5,11.77 10.83,12.45 10,12.45c "/> + <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M6.47 15.23 C6.27,15.23 6.08,15.16 5.94,15.01 C4.85,13.93 4.25,12.48 4.25,10.95 C4.25,9.41 4.85,7.97 5.94,6.88 C8.18,4.64 11.82,4.64 14.07,6.88 C15.15,7.97 15.75,9.41 15.75,10.95 C15.75,12.48 15.15,13.93 14.07,15.01 C13.77,15.3 13.3,15.3 13.01,15.01 C12.71,14.72 12.71,14.24 13.01,13.95 C13.81,13.15 14.25,12.08 14.25,10.95 C14.25,9.81 13.81,8.74 13.01,7.94 C11.35,6.28 8.65,6.28 6.99,7.94 C6.19,8.75 5.75,9.81 5.75,10.95 C5.75,12.08 6.19,13.15 6.99,13.95 C7.29,14.25 7.29,14.72 6.99,15.01 C6.85,15.16 6.66,15.23 6.47,15.23c "/> + <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M16.36 18.06 C16.17,18.06 15.98,17.99 15.83,17.84 C15.54,17.55 15.54,17.07 15.83,16.78 C17.39,15.22 18.25,13.15 18.25,10.95 C18.25,8.74 17.39,6.67 15.83,5.11 C12.62,1.9 7.38,1.9 4.17,5.11 C2.61,6.67 1.75,8.74 1.75,10.95 C1.75,13.15 2.61,15.22 4.17,16.78 C4.46,17.07 4.46,17.55 4.17,17.84 C3.87,18.13 3.4,18.13 3.11,17.84 C1.26,16 0.25,13.55 0.25,10.95 C0.25,8.34 1.26,5.89 3.11,4.05 C6.91,0.25 13.09,0.25 16.89,4.05 C18.74,5.89 19.75,8.34 19.75,10.95 C19.75,13.55 18.74,16 16.89,17.84 C16.75,17.99 16.56,18.06 16.36,18.06c "/> + </group> + </group> + <group android:name="time_group"/> +</vector> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml new file mode 100644 index 000000000000..06755f6e3871 --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_signal_wifi_transient_animation_drawable"> + <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_signal_wifi_transient_animation_0"/> + <target android:name="_R_G_L_0_G_D_1_P_0" android:animation="@*android:anim/ic_signal_wifi_transient_animation_1"/> + <target android:name="_R_G_L_0_G_D_2_P_0" android:animation="@*android:anim/ic_signal_wifi_transient_animation_2"/> + <target android:name="_R_G_L_0_G_D_3_P_0" android:animation="@*android:anim/ic_signal_wifi_transient_animation_3"/> + <target android:name="time_group" android:animation="@*android:anim/ic_signal_wifi_transient_animation_4"/> +</animated-vector> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml new file mode 100644 index 000000000000..6e455133e6eb --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_0_G" android:translateX="0.6440000000000001" + android:translateY="2.755000000000001"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="0.3" android:fillType="nonZero" + android:pathData=" M12.86 15.75 C12.86,16.58 12.19,17.25 11.36,17.25 C10.53,17.25 9.86,16.58 9.86,15.75 C9.86,14.92 10.53,14.25 11.36,14.25 C12.19,14.25 12.86,14.92 12.86,15.75c "/> + <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="0.3" android:fillType="nonZero" + android:pathData=" M15.61 12.25 C15.42,12.25 15.23,12.17 15.08,12.03 C14.09,11.04 12.76,10.5 11.36,10.5 C9.95,10.5 8.63,11.04 7.63,12.03 C7.34,12.32 6.86,12.32 6.57,12.02 C6.28,11.73 6.28,11.26 6.58,10.96 C7.86,9.7 9.55,9 11.36,9 C13.16,9 14.86,9.7 16.14,10.96 C16.43,11.25 16.43,11.73 16.14,12.02 C16,12.17 15.8,12.25 15.61,12.25c "/> + <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000" + android:fillAlpha="0.3" android:fillType="nonZero" + android:pathData=" M18.77 9.08 C18.58,9.08 18.39,9.01 18.24,8.86 C16.4,7.02 13.96,6 11.36,6 C8.75,6 6.31,7.02 4.47,8.86 C4.18,9.16 3.7,9.16 3.41,8.86 C3.12,8.57 3.11,8.1 3.41,7.8 C5.53,5.67 8.35,4.5 11.36,4.5 C14.36,4.5 17.18,5.67 19.31,7.8 C19.6,8.1 19.6,8.57 19.3,8.86 C19.16,9.01 18.97,9.08 18.77,9.08c "/> + <path android:name="_R_G_L_0_G_D_3_P_0" android:fillColor="#000000" + android:fillAlpha="0.3" android:fillType="nonZero" + android:pathData=" M21.96 5.89 C21.77,5.89 21.58,5.82 21.43,5.67 C18.74,2.98 15.16,1.5 11.36,1.5 C7.55,1.5 3.97,2.98 1.28,5.67 C0.99,5.97 0.51,5.97 0.22,5.67 C-0.07,5.38 -0.07,4.91 0.22,4.61 C3.19,1.64 7.15,0 11.36,0 C15.56,0 19.52,1.64 22.49,4.61 C22.78,4.91 22.78,5.38 22.49,5.67 C22.35,5.82 22.15,5.89 21.96,5.89c "/> + </group> + </group> + <group android:name="time_group"/> +</vector> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/values/config.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/values/config.xml new file mode 100644 index 000000000000..ae5cc2bf9883 --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/values/config.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright 2019, 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. + */ +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="config_batterymeterPerimeterPath" translatable="false"> + M 8,1 C 8,0.45 7.55,0 7,0 H 5 C 4.45,0 4,0.45 4,1 H 3 C 1.34,1 0,2.34 0,4 V 17 C 0,18.66 1.34,20 3,20 H 9 C 10.66,20 12,18.66 12,17 V 4 C 12,2.34 10.66,1 9,1 Z M 10.5,4 V 17 C 10.5,17.83 9.83,18.5 9,18.5 H 3 C 2.17,18.5 1.5,17.83 1.5,17 V 4 C 1.5,3.17 2.17,2.5 3,2.5 H 9 C 9.83,2.5 10.5,3.17 10.5,4 Z + </string> + <string name="config_batterymeterFillMask" translatable="false"> + M 10.5,4 V 17 C 10.5,17.83 9.83,18.5 9,18.5 H 3 C 2.17,18.5 1.5,17.83 1.5,17 V 4 C 1.5,3.17 2.17,2.5 3,2.5 H 9 C 9.83,2.5 10.5,3.17 10.5,4 Z + </string> + <string name="config_batterymeterBoltPath" translatable="false"> + M 8.08,9.5 H 7 V 5.99 C 7,5.73 6.65,5.64 6.53,5.87 L 3.7,11.13 C 3.61,11.3 3.73,11.5 3.92,11.5 H 5 V 15.01 C 5,15.27 5.35,15.36 5.47,15.13 L 8.3,9.87 C 8.39,9.7 8.27,9.5 8.08,9.5 Z + </string> + <string name="config_batterymeterPowersavePath" translatable="false"> + M 3.75,11.25 H 5.25 V 12.75 C 5.25,13.16 5.59,13.5 6,13.5 6.41,13.5 6.75,13.16 6.75,12.75 V 11.25 H 8.25 C 8.66,11.25 9,10.91 9,10.5 9,10.09 8.6601,9.75 8.25,9.75 H 6.75 V 8.25 C 6.75,7.84 6.41,7.5 6,7.5 5.59,7.5 5.25,7.84 5.25,8.25 V 9.75 H 3.75 C 3.34,9.75 3,10.09 3,10.5 3,10.91 3.34,11.25 3.75,11.25 Z + </string> +</resources> diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/Android.mk b/packages/overlays/IconPackCircularThemePickerOverlay/Android.mk new file mode 100644 index 000000000000..412c26fc3da4 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/Android.mk @@ -0,0 +1,31 @@ +# +# Copyright 2019, 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. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_RRO_THEME := IconPackCircularThemePicker +LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := IconPackCircularThemePickerOverlay +LOCAL_SDK_VERSION := current + +include $(BUILD_RRO_PACKAGE) diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/AndroidManifest.xml b/packages/overlays/IconPackCircularThemePickerOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..eae7de8f5284 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/AndroidManifest.xml @@ -0,0 +1,24 @@ +<!-- +/** + * Copyright (c) 2019, 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. + */ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.theme.icon_pack.circular.themepicker" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="com.android.wallpaper" android:category="android.theme.customization.icon_pack.themepicker" android:priority="1"/> + <application android:label="Circular" android:hasCode="false"/> +</manifest> diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_add_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_add_24px.xml new file mode 100644 index 000000000000..900aaa0371b4 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_add_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,11.25h-7.5v-7.5C12.75,3.34,12.41,3,12,3s-0.75,0.34-0.75,0.75v7.5h-7.5C3.34,11.25,3,11.59,3,12 s0.34,0.75,0.75,0.75h7.5v7.5c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-7.5h7.5c0.41,0,0.75-0.34,0.75-0.75 S20.66,11.25,20.25,11.25z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_close_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_close_24px.xml new file mode 100644 index 000000000000..ddfb980bea88 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_close_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M20.03,3.97c-0.29-0.29-0.77-0.29-1.06,0L12,10.94L5.03,3.97c-0.29-0.29-0.77-0.29-1.06,0s-0.29,0.77,0,1.06L10.94,12 l-6.97,6.97c-0.29,0.29-0.29,0.77,0,1.06c0.15,0.15,0.34,0.22,0.53,0.22s0.38-0.07,0.53-0.22L12,13.06l6.97,6.97 c0.15,0.15,0.34,0.22,0.53,0.22s0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06L13.06,12l6.97-6.97 C20.32,4.74,20.32,4.26,20.03,3.97z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_colorize_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_colorize_24px.xml new file mode 100644 index 000000000000..f572af605268 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_colorize_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M17.59,3.59l-2.97,2.97l-2.09-2.09c-0.29-0.29-0.77-0.29-1.06,0c-0.29,0.29-0.29,0.77,0,1.06l1.5,1.5l-9.68,9.68 C3.11,16.89,3,17.15,3,17.41V21h3.59c0.27,0,0.52-0.11,0.71-0.29l9.68-9.68l1.5,1.5c0.15,0.15,0.34,0.22,0.53,0.22 c0.19,0,0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06l-2.09-2.09l2.97-2.97c0.78-0.78,0.78-2.05,0-2.83 C19.63,2.8,18.37,2.8,17.59,3.59z M6.38,19.5H4.5v-1.88l9.5-9.5L15.88,10L6.38,19.5z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_delete_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_delete_24px.xml new file mode 100644 index 000000000000..a87186bf2966 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_delete_24px.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M9,20h6c1.66,0,3-1.34,3-3V6h0.5c0.41,0,0.75-0.34,0.75-0.75S18.91,4.5,18.5,4.5H18h-3l-1-1h-4l-1,1H6H5.5 c-0.41,0-0.75,0.34-0.75,0.75S5.09,6,5.5,6H6v11C6,18.66,7.34,20,9,20z M16.5,6v11c0,0.83-0.67,1.5-1.5,1.5H9 c-0.83,0-1.5-0.67-1.5-1.5V6H16.5z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M13.97,16c0.41,0,0.75-0.34,0.75-0.75v-6.5c0-0.41-0.34-0.75-0.75-0.75s-0.75,0.34-0.75,0.75v6.5 C13.22,15.66,13.55,16,13.97,16z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M10,16c0.41,0,0.75-0.34,0.75-0.75v-6.5C10.75,8.34,10.41,8,10,8S9.25,8.34,9.25,8.75v6.5C9.25,15.66,9.59,16,10,16z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_font.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_font.xml new file mode 100644 index 000000000000..edaf3c7e0b42 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_font.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M18,2H6C3.8,2,2,3.8,2,6v12c0,2.2,1.8,4,4,4h12c2.2,0,4-1.8,4-4V6C22,3.8,20.2,2,18,2z M20.5,18c0,1.38-1.12,2.5-2.5,2.5H6 c-1.38,0-2.5-1.12-2.5-2.5V6c0-1.38,1.12-2.5,2.5-2.5h12c1.38,0,2.5,1.12,2.5,2.5V18z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M10.69,6L6.2,18h2.5l1-2.87h4.59L15.3,18h2.5L13.29,6H10.69z M10.43,13.06l1.51-4.46h0.13l1.49,4.46H10.43z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_clock.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_clock.xml new file mode 100644 index 000000000000..2884d71cefea --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_clock.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M22,12c0-5.52-4.48-10-10-10C6.48,2,2,6.48,2,12s4.48,10,10,10C17.52,22,22,17.52,22,12z M12,20.5 c-4.69,0-8.5-3.81-8.5-8.5S7.31,3.5,12,3.5c4.69,0,8.5,3.81,8.5,8.5S16.69,20.5,12,20.5z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M12.75,11.69V5.78c0-0.41-0.34-0.75-0.75-0.75s-0.75,0.34-0.75,0.75v6.53l3.78,3.78c0.15,0.15,0.34,0.22,0.53,0.22 s0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06L12.75,11.69z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_grid.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_grid.xml new file mode 100644 index 000000000000..d50dbd4e0cac --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_grid.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M2.75,16.75C2.34,16.75,2,17.09,2,17.5s0.34,0.75,0.75,0.75h3v3C5.75,21.66,6.09,22,6.5,22s0.75-0.34,0.75-0.75v-3h4v3 c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-3h4v3c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-3h3 c0.41,0,0.75-0.34,0.75-0.75s-0.34-0.75-0.75-0.75h-3v-4h3c0.41,0,0.75-0.34,0.75-0.75s-0.34-0.75-0.75-0.75h-3v-4h3 C21.66,7.25,22,6.91,22,6.5s-0.34-0.75-0.75-0.75h-3v-3C18.25,2.34,17.91,2,17.5,2s-0.75,0.34-0.75,0.75v3h-4v-3 C12.75,2.34,12.41,2,12,2s-0.75,0.34-0.75,0.75v3h-4v-3C7.25,2.34,6.91,2,6.5,2S5.75,2.34,5.75,2.75v3h-3C2.34,5.75,2,6.09,2,6.5 s0.34,0.75,0.75,0.75h3v4h-3C2.34,11.25,2,11.59,2,12s0.34,0.75,0.75,0.75h3v4H2.75z M16.75,16.75h-4v-4h4V16.75z M16.75,7.25v4h-4 v-4H16.75z M7.25,7.25h4v4h-4V7.25z M7.25,12.75h4v4h-4V12.75z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_theme.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_theme.xml new file mode 100644 index 000000000000..7375bc931665 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_theme.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M8,14h1v5c0,1.66,1.34,3,3,3s3-1.34,3-3v-5h1c1.66,0,3-1.34,3-3V2H5v9C5,12.66,6.34,14,8,14z M16,12.5h-2.5V14v5 c0,0.83-0.67,1.5-1.5,1.5s-1.5-0.67-1.5-1.5v-5v-1.5H8c-0.83,0-1.5-0.67-1.5-1.5v-0.5h11V11C17.5,11.83,16.83,12.5,16,12.5z M9,3.5 v3c0,0.41,0.34,0.75,0.75,0.75S10.5,6.91,10.5,6.5v-3h2.75v3c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-3h2.75V9h-11V3.5H9z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml new file mode 100644 index 000000000000..bdb74424c82e --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M 16 6.75 C 16.6903559373 6.75 17.25 7.30964406271 17.25 8 C 17.25 8.69035593729 16.6903559373 9.25 16 9.25 C 15.3096440627 9.25 14.75 8.69035593729 14.75 8 C 14.75 7.30964406271 15.3096440627 6.75 16 6.75 Z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M11,3.76c0-0.41-0.34-0.75-0.75-0.75H6.84C4.72,3.01,3,4.74,3,6.85v3.4C3,10.66,3.34,11,3.75,11s0.75-0.34,0.75-0.75v-3.4 c0-1.29,1.05-2.33,2.34-2.33h3.41C10.66,4.51,11,4.18,11,3.76z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M10.25,19.5H6.84c-1.29,0-2.34-1.05-2.34-2.34v-3.41C4.5,13.34,4.16,13,3.75,13S3,13.34,3,13.75v3.41 C3,19.28,4.72,21,6.84,21h3.41c0.41,0,0.75-0.34,0.75-0.75S10.66,19.5,10.25,19.5z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,13c-0.41,0-0.75,0.34-0.75,0.75v3.41c0,1.29-1.05,2.34-2.34,2.34h-3.41c-0.41,0-0.75,0.34-0.75,0.75 S13.34,21,13.75,21h3.41c2.12,0,3.84-1.72,3.84-3.84v-3.41C21,13.34,20.66,13,20.25,13z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M17.16,3h-3.41C13.34,3,13,3.34,13,3.75s0.34,0.75,0.75,0.75h3.41c1.29,0,2.34,1.05,2.34,2.34v3.41 c0,0.41,0.34,0.75,0.75,0.75S21,10.66,21,10.25V6.84C21,4.72,19.28,3,17.16,3z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M12.89,15.29l-1.41-1.82c-0.8-1.04-2.37-1.03-3.17,0.01l-2.7,3.51l12.83-0.01l-1.88-2.35c-0.79-0.99-2.29-1-3.1-0.03 L12.89,15.29z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_shapes_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_shapes_24px.xml new file mode 100644 index 000000000000..b7e6bf9e9a12 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_shapes_24px.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M19,9h-1.5v1.5H19c0.83,0,1.5,0.67,1.5,1.5v7c0,0.83-0.67,1.5-1.5,1.5h-8c-0.83,0-1.5-0.67-1.5-1.5v-1.5H8V19 c0,1.66,1.34,3,3,3h8c1.66,0,3-1.34,3-3v-7C22,10.34,20.66,9,19,9z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M9,16c3.87,0,7-3.13,7-7c0-3.87-3.13-7-7-7C5.13,2,2,5.13,2,9C2,12.87,5.13,16,9,16z M3.5,9c0-3.03,2.47-5.5,5.5-5.5 s5.5,2.47,5.5,5.5s-2.47,5.5-5.5,5.5S3.5,12.03,3.5,9z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_tune.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_tune.xml new file mode 100644 index 000000000000..9c8821152466 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_tune.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="20dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="20dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,4.75h-2.4C17.55,4.02,16.84,3.5,16,3.5c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2c0.84,0,1.55-0.52,1.85-1.25h2.4 C20.66,6.25,21,5.91,21,5.5S20.66,4.75,20.25,4.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M11.98,4.75H3.75C3.34,4.75,3,5.09,3,5.5s0.34,0.75,0.75,0.75h8.23c0.01,0,0.01,0,0.02,0V4.75 C11.99,4.75,11.99,4.75,11.98,4.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,17.75h-5.4c-0.3-0.73-1.01-1.25-1.85-1.25c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2c0.84,0,1.55-0.52,1.85-1.25h5.4 c0.41,0,0.75-0.34,0.75-0.75S20.66,17.75,20.25,17.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M8.98,17.75H3.75C3.34,17.75,3,18.09,3,18.5s0.34,0.75,0.75,0.75h5.23c0.01,0,0.01,0,0.02,0v-1.49 C8.99,17.75,8.99,17.75,8.98,17.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M7,10c-0.84,0-1.55,0.52-1.85,1.25h-1.4C3.34,11.25,3,11.59,3,12s0.34,0.75,0.75,0.75h1.4C5.45,13.48,6.16,14,7,14 c1.1,0,2-0.9,2-2C9,10.9,8.1,10,7,10z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,11.25h-9.23c-0.01,0-0.01,0-0.02,0v1.49c0.01,0,0.01,0,0.02,0h9.23c0.41,0,0.75-0.34,0.75-0.75 S20.66,11.25,20.25,11.25z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_wifi_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_wifi_24px.xml new file mode 100644 index 000000000000..fde996584f57 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_wifi_24px.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M 12 17 C 12.8284271247 17 13.5 17.6715728753 13.5 18.5 C 13.5 19.3284271247 12.8284271247 20 12 20 C 11.1715728753 20 10.5 19.3284271247 10.5 18.5 C 10.5 17.6715728753 11.1715728753 17 12 17 Z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M19.42,11.84c-0.19,0-0.38-0.07-0.53-0.22C17.05,9.77,14.6,8.75,12,8.75s-5.05,1.02-6.89,2.86 c-0.29,0.29-0.77,0.29-1.06,0c-0.29-0.29-0.29-0.77,0-1.06C6.17,8.43,9,7.25,12,7.25s5.83,1.17,7.95,3.3 c0.29,0.29,0.29,0.77,0,1.06C19.8,11.76,19.61,11.84,19.42,11.84z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M22.61,8.65c-0.19,0-0.38-0.07-0.53-0.22C19.38,5.74,15.81,4.25,12,4.25S4.62,5.74,1.92,8.43c-0.29,0.29-0.77,0.29-1.06,0 s-0.29-0.77,0-1.06C3.84,4.39,7.79,2.75,12,2.75s8.16,1.64,11.14,4.61c0.29,0.29,0.29,0.77,0,1.06 C22.99,8.57,22.8,8.65,22.61,8.65z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M16.25,15c-0.19,0-0.38-0.07-0.53-0.22c-1-0.99-2.32-1.53-3.73-1.53s-2.73,0.54-3.73,1.53c-0.29,0.29-0.77,0.29-1.06-0.01 s-0.29-0.77,0.01-1.06c1.28-1.27,2.98-1.96,4.78-1.96s3.5,0.7,4.78,1.96c0.29,0.29,0.3,0.77,0.01,1.06 C16.64,14.93,16.45,15,16.25,15z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml new file mode 100644 index 000000000000..e884bd3bc249 --- /dev/null +++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_bluetooth_transient_animation_drawable"> + <target android:name="_R_G_L_1_G_D_0_P_0" android:animation="@*android:anim/ic_bluetooth_transient_animation_0" /> + <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_bluetooth_transient_animation_1" /> + <target android:name="time_group" android:animation="@*android:anim/ic_bluetooth_transient_animation_2" /> +</animated-vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml new file mode 100644 index 000000000000..79107d8f6214 --- /dev/null +++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" + android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_2_G" android:translateX="3.099" + android:translateY="1.6400000000000006"> + <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M10.41 10.36 C10.41,10.36 14.16,6.62 14.16,6.62 C14.57,6.21 14.57,5.55 14.16,5.14 C14.16,5.14 9.69,0.67 9.69,0.67 C9.69,0.67 9.66,0.64 9.66,0.64 C9.24,0.25 8.58,0.27 8.18,0.69 C8,0.88 7.9,1.14 7.9,1.4 C7.9,1.4 7.9,7.84 7.9,7.84 C7.9,7.84 3.85,3.79 3.85,3.79 C3.44,3.38 2.79,3.38 2.38,3.79 C1.97,4.2 1.97,4.85 2.38,5.26 C2.38,5.26 7.47,10.36 7.47,10.36 C7.47,10.36 2.38,15.45 2.38,15.45 C1.97,15.86 1.97,16.51 2.38,16.92 C2.79,17.33 3.44,17.33 3.85,16.92 C3.85,16.92 7.9,12.87 7.9,12.87 C7.9,12.87 7.9,19.32 7.9,19.32 C7.9,19.89 8.37,20.36 8.94,20.36 C9.2,20.36 9.46,20.26 9.65,20.08 C9.65,20.08 9.7,20.03 9.7,20.03 C9.7,20.03 14.16,15.57 14.16,15.57 C14.57,15.16 14.57,14.5 14.16,14.09 C14.16,14.09 10.41,10.36 10.41,10.36c M9.89 3.73 C9.89,3.73 12.04,5.88 12.04,5.88 C12.04,5.88 9.89,8.03 9.89,8.03 C9.89,8.03 9.89,3.73 9.89,3.73c M9.89 16.98 C9.89,16.98 9.89,12.68 9.89,12.68 C9.89,12.68 12.04,14.83 12.04,14.83 C12.04,14.83 9.89,16.98 9.89,16.98c "/> + </group> + <group android:name="_R_G_L_1_G" android:translateX="3.099" + android:translateY="1.6400000000000006"> + <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M16.96 9.3 C16.95,9.3 16.95,9.29 16.95,9.28 C16.36,8.71 15.42,8.72 14.84,9.3 C14.84,9.3 14.83,9.31 14.83,9.31 C14.25,9.9 14.25,10.84 14.84,11.42 C15.42,12.01 16.37,12.01 16.96,11.42 C17.55,10.84 17.55,9.89 16.96,9.3c "/> + </group> + <group android:name="_R_G_L_0_G" android:translateX="3.099" + android:translateY="1.6400000000000006"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M2.96 9.3 C2.96,9.3 2.95,9.29 2.95,9.29 C2.36,8.71 1.42,8.71 0.84,9.3 C0.84,9.3 0.83,9.31 0.83,9.31 C0.25,9.9 0.25,10.84 0.84,11.42 C1.42,12.01 2.37,12.01 2.96,11.42 C3.55,10.83 3.55,9.89 2.96,9.3c "/> + </group> + </group> + <group android:name="time_group"/> +</vector> diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml new file mode 100644 index 000000000000..54738c083ca3 --- /dev/null +++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_hotspot_transient_animation_drawable"> + <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_2"/> + <target android:name="_R_G_L_0_G_D_1_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_1"/> + <target android:name="_R_G_L_0_G_D_2_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_0"/> + <target android:name="time_group" android:animation="@*android:anim/ic_hotspot_transient_animation_3"/> +</animated-vector> diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml new file mode 100644 index 000000000000..737b5222c181 --- /dev/null +++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" + android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_0_G" android:translateX="1.545" + android:translateY="2.1449999999999996"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M9.31 0.92 C4.67,1.43 0.92,5.26 0.5,9.92 C0.25,12.64 1.1,15.17 2.65,17.1 C3.02,17.56 3.71,17.6 4.13,17.18 C4.49,16.82 4.52,16.25 4.21,15.86 C2.81,14.11 2.12,11.76 2.61,9.25 C3.22,6.12 5.75,3.6 8.89,3 C14,2.04 18.45,5.92 18.45,10.86 C18.45,12.75 17.79,14.48 16.69,15.86 C16.37,16.25 16.41,16.81 16.77,17.17 C16.77,17.17 16.77,17.17 16.77,17.17 C17.19,17.59 17.89,17.56 18.26,17.09 C19.64,15.39 20.45,13.22 20.45,10.86 C20.45,4.96 15.34,0.25 9.31,0.92c "/> + <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M13.91 14.31 C13.91,14.31 13.92,14.32 13.92,14.32 C14.35,14.75 15.08,14.71 15.42,14.21 C16.07,13.25 16.45,12.1 16.45,10.85 C16.45,7.11 13.01,4.16 9.12,5 C6.88,5.49 5.08,7.3 4.6,9.54 C4.22,11.28 4.61,12.92 5.48,14.2 C5.83,14.71 6.56,14.75 6.99,14.32 C6.99,14.32 7,14.31 7,14.31 C7.34,13.97 7.37,13.43 7.1,13.03 C6.6,12.26 6.36,11.32 6.49,10.29 C6.73,8.55 8.16,7.13 9.9,6.89 C12.36,6.56 14.46,8.46 14.46,10.85 C14.46,11.66 14.22,12.4 13.81,13.02 C13.54,13.43 13.57,13.97 13.91,14.31c "/> + <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.46 10.86 C12.46,11.96 11.56,12.86 10.46,12.86 C9.35,12.86 8.46,11.96 8.46,10.86 C8.46,9.75 9.35,8.86 10.46,8.86 C11.56,8.86 12.46,9.75 12.46,10.86c "/> + </group> + </group> + <group android:name="time_group"/> +</vector> diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml new file mode 100644 index 000000000000..49d235b03be2 --- /dev/null +++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_signal_wifi_transient_animation_drawable"> + <target android:name="_R_G_L_7_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_0"/> + <target android:name="_R_G_L_6_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_1"/> + <target android:name="_R_G_L_6_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_2"/> + <target android:name="_R_G_L_5_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_1"/> + <target android:name="_R_G_L_5_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_2"/> + <target android:name="_R_G_L_4_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_3"/> + <target android:name="_R_G_L_4_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_4"/> + <target android:name="_R_G_L_3_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_3"/> + <target android:name="_R_G_L_3_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_4"/> + <target android:name="_R_G_L_2_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_5"/> + <target android:name="_R_G_L_2_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_6"/> + <target android:name="_R_G_L_1_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_5"/> + <target android:name="_R_G_L_1_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_6"/> + <target android:name="_R_G_L_0_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_7"/> + <target android:name="time_group" android:animation="@*android:anim/ic_signal_wifi_transient_animation_8"/> +</animated-vector> diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml new file mode 100644 index 000000000000..9fcb4795ad68 --- /dev/null +++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_7_G" android:translateX="0.10000000000000142" + android:translateY="3"> + <path android:name="_R_G_L_7_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="0.3" android:fillType="nonZero" + android:pathData=" M23.56 5.11 C23.95,4.63 23.85,3.92 23.34,3.57 C21.57,2.36 17.45,0 11.9,0 C6.34,0 2.23,2.36 0.46,3.57 C-0.05,3.92 -0.15,4.63 0.23,5.11 C0.23,5.11 11.06,18.6 11.06,18.6 C11.48,19.13 12.29,19.13 12.72,18.6 C12.72,18.6 23.56,5.11 23.56,5.11c "/> + </group> + <group android:name="_R_G_L_6_G" android:translateX="0.10000000000000142" + android:translateY="3"> + <path android:name="_R_G_L_6_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="0.3" android:fillType="nonZero" + android:pathData=" M23.56 5.11 C23.95,4.63 23.85,3.92 23.34,3.57 C21.57,2.36 17.45,0 11.9,0 C6.34,0 2.23,2.36 0.46,3.57 C-0.05,3.92 -0.15,4.63 0.23,5.11 C0.23,5.11 11.06,18.6 11.06,18.6 C11.48,19.13 12.29,19.13 12.72,18.6 C12.72,18.6 23.56,5.11 23.56,5.11c "/> + </group> + <group android:name="_R_G_L_5_G" android:translateX="5.81" + android:translateY="12.747"> + <path android:name="_R_G_L_5_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M7.01 8.85 C7.01,8.85 12.12,2.5 12.12,2.5 C10.48,1.05 8.37,0.25 6.19,0.25 C3.91,0.25 1.84,1.1 0.25,2.5 C0.25,2.5 5.35,8.85 5.35,8.85 C5.78,9.38 6.58,9.38 7.01,8.85c "/> + </group> + <group android:name="_R_G_L_4_G" android:translateX="0.10000000000000142" + android:translateY="3"> + <path android:name="_R_G_L_4_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="0.3" android:fillType="nonZero" + android:pathData=" M23.56 5.11 C23.95,4.63 23.85,3.92 23.34,3.57 C21.57,2.36 17.45,0 11.9,0 C6.34,0 2.23,2.36 0.46,3.57 C-0.05,3.92 -0.15,4.63 0.23,5.11 C0.23,5.11 11.06,18.6 11.06,18.6 C11.48,19.13 12.29,19.13 12.72,18.6 C12.72,18.6 23.56,5.11 23.56,5.11c "/> + </group> + <group android:name="_R_G_L_3_G" android:translateX="3.9290000000000003" + android:translateY="9.75"> + <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M8.89 11.85 C8.89,11.85 15.88,3.15 15.88,3.15 C13.78,1.35 11.06,0.25 8.07,0.25 C5.08,0.25 2.35,1.35 0.25,3.16 C0.25,3.16 7.23,11.86 7.23,11.86 C7.66,12.38 8.46,12.38 8.89,11.85c "/> + </group> + <group android:name="_R_G_L_2_G" android:translateX="0.10000000000000142" + android:translateY="3"> + <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="0.3" android:fillType="nonZero" + android:pathData=" M23.56 5.11 C23.95,4.63 23.85,3.92 23.34,3.57 C21.57,2.36 17.45,0 11.9,0 C6.34,0 2.23,2.36 0.46,3.57 C-0.05,3.92 -0.15,4.63 0.23,5.11 C0.23,5.11 11.06,18.6 11.06,18.6 C11.48,19.13 12.29,19.13 12.72,18.6 C12.72,18.6 23.56,5.11 23.56,5.11c "/> + </group> + <group android:name="_R_G_L_1_G" android:translateX="2.6799999999999997" + android:translateY="7.748"> + <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M10.14 13.85 C10.14,13.85 18.39,3.59 18.39,3.59 C15.86,1.43 12.65,0.25 9.32,0.25 C5.86,0.25 2.69,1.51 0.25,3.6 C0.25,3.6 8.48,13.86 8.48,13.86 C8.91,14.38 9.71,14.38 10.14,13.85c "/> + </group> + <group android:name="_R_G_L_0_G" android:translateX="-0.3000000000000007" + android:translateY="2.75"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.3 0.25 C6.74,0.25 2.63,2.61 0.86,3.82 C0.35,4.17 0.25,4.88 0.63,5.36 C0.63,5.36 11.46,18.85 11.46,18.85 C11.88,19.38 12.69,19.38 13.12,18.85 C13.12,18.85 23.96,5.36 23.96,5.36 C24.35,4.88 24.25,4.17 23.74,3.82 C21.97,2.61 17.85,0.25 12.3,0.25c "/> + </group> + </group> + <group android:name="time_group"/> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/values/config.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/values/config.xml new file mode 100644 index 000000000000..6b59b6265c54 --- /dev/null +++ b/packages/overlays/IconPackFilledAndroidOverlay/res/values/config.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2019, 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. + */ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="config_batterymeterPerimeterPath" translatable="false"> + M 4,0 V 2 H 2.33 C 1.6,2 1,2.6 1,3.33 V 18.66 C 1,19.4 1.6,20 2.33,20 H 9.66 C 10.4,20 11,19.4 11,18.67 V 3.33 C 11,2.6 10.4,2 9.67,2 H 8 V 0 Z + </string> + <string name="config_batterymeterErrorPerimeterPath" translatable="false"> + M 3.5,0 V 0.5 1.5 H 2.3301 C 1.3261,1.5 0.5,2.3261 0.5,3.3301 V 18.16 C 0.5,19.17 1.3261,20 2.3301,20 H 9.6602 C 10.67,20 11.5,19.174 11.5,18.17 V 3.3301 C 11.5,2.3261 10.674,1.5 9.6699,1.5 H 8.5 V 0 Z M 9.1698,2.9999 C 9.6259,2.9999 9.9999,3.374 9.9999,3.83 V 17.67 C 9.9999,18.126 9.6299,18.5 9.1601,18.5 H 2.83 C 2.3741,18.5 2,18.13 2,17.66 V 3.83 C 2,3.374 2.3741,2.9999 2.83,2.9999 Z + </string> + <string name="config_batterymeterFillMask" translatable="false"> + M 4,0 V 2 H 2.33 C 1.6,2 1,2.6 1,3.33 V 18.66 C 1,19.4 1.6,20 2.33,20 H 9.66 C 10.4,20 11,19.4 11,18.67 V 3.33 C 11,2.6 10.4,2 9.67,2 H 8 V 0 Z + </string> + <string name="config_batterymeterBoltPath" translatable="false"> + M 8.58,10 C 8.77,10 8.89,10.2 8.8,10.37 L 5.94,15.74 C 5.7,16.19 5,16.02 5,15.5 V 12 H 3.42 C 3.23,12 3.11,11.8 3.2,11.63 L 6.06,6.26 C 6.3,5.81 7,5.98 7,6.5 V 10 Z + </string> + <string name="config_batterymeterPowersavePath" translatable="false"> + M 9,11 C 9,11.55 8.55,12 8,12 H 7 V 13 C 7,13.55 6.55,14 6,14 5.45,14 5,13.55 5,13 V 12 H 4 C 3.45,12 3,11.55 3,11 3,10.45 3.45,10.005 4,10 H 5 V 9 C 5,8.45 5.45,8 6,8 6.55,8 7,8.45 7,9 V 10 H 8 C 8.55,10 9,10.45 9,11 Z + </string> + <bool name="config_batterymeterDualTone">true</bool> +</resources> diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/Android.mk b/packages/overlays/IconPackFilledThemePickerOverlay/Android.mk new file mode 100644 index 000000000000..6d15603663b5 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/Android.mk @@ -0,0 +1,31 @@ +# +# Copyright 2019, 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. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_RRO_THEME := IconPackFilledThemePicker +LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := IconPackFilledThemePickerOverlay +LOCAL_SDK_VERSION := current + +include $(BUILD_RRO_PACKAGE) diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/AndroidManifest.xml b/packages/overlays/IconPackFilledThemePickerOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..35023ab9653e --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/AndroidManifest.xml @@ -0,0 +1,24 @@ +<!-- +/** + * Copyright (c) 2019, 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. + */ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.theme.icon_pack.filled.themepicker" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="com.android.wallpaper" android:category="android.theme.customization.icon_pack.themepicker" android:priority="1"/> + <application android:label="Filled" android:hasCode="false"/> +</manifest> diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_add_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_add_24px.xml new file mode 100644 index 000000000000..1768723b65e9 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_add_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M5,13h6v6c0,0.55,0.45,1,1,1s1-0.45,1-1v-6h6c0.55,0,1-0.45,1-1s-0.45-1-1-1h-6V5c0-0.55-0.45-1-1-1s-1,0.45-1,1v6H5 c-0.55,0-1,0.45-1,1S4.45,13,5,13z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_close_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_close_24px.xml new file mode 100644 index 000000000000..4bfff2cb3ad8 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_close_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M5.7,18.3c0.39,0.39,1.02,0.39,1.41,0L12,13.41l4.89,4.89c0.39,0.39,1.02,0.39,1.41,0s0.39-1.02,0-1.41L13.41,12l4.89-4.89 c0.38-0.38,0.38-1.02,0-1.4c-0.39-0.39-1.02-0.39-1.41,0c0,0,0,0,0,0L12,10.59L7.11,5.7c-0.39-0.39-1.02-0.39-1.41,0 s-0.39,1.02,0,1.41L10.59,12L5.7,16.89C5.31,17.28,5.31,17.91,5.7,18.3z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_colorize_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_colorize_24px.xml new file mode 100644 index 000000000000..aa3a925b50c8 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_colorize_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M11.21,5.2L11.21,5.2c-0.39,0.39-0.39,1.02,0,1.41l0.71,0.71l-8.63,8.63C3.11,16.14,3,16.4,3,16.66V20c0,0.55,0.45,1,1,1 h3.34c0.27,0,0.52-0.11,0.71-0.29l8.63-8.63l0.72,0.72c0.39,0.39,1.02,0.39,1.41,0c0.39-0.39,0.39-1.02,0-1.41l-1.21-1.22L20,7.75 c0.78-0.78,0.78-2.05,0-2.83L19.08,4c-0.78-0.78-2.05-0.78-2.83,0l-2.41,2.41L12.61,5.2C12.23,4.81,11.6,4.81,11.21,5.2z M14.98,10.94L6.92,19H5v-1.92l8.06-8.06L14.98,10.94z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_delete_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_delete_24px.xml new file mode 100644 index 000000000000..94c63118cf76 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_delete_24px.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M18,4h-2.5l-0.71-0.71C14.61,3.11,14.35,3,14.09,3H9.9C9.64,3,9.38,3.11,9.2,3.29L8.49,4h-2.5c-0.55,0-1,0.45-1,1 s0.45,1,1,1h12c0.55,0,1-0.45,1-1C19,4.45,18.55,4,18,4z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M6,19c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2V7H6V19z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_font.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_font.xml new file mode 100644 index 000000000000..760382385f79 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_font.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M 12.07 8.6 L 11.94 8.6 L 10.43 13.06 L 13.56 13.06 Z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M19,2H5C3.35,2,2,3.35,2,5v14c0,1.65,1.35,3,3,3h14c1.65,0,3-1.35,3-3V5C22,3.35,20.65,2,19,2z M15.3,18l-1.01-2.87H9.7 L8.7,18H6.2l4.49-12h2.6l4.51,12H15.3z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_clock.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_clock.xml new file mode 100644 index 000000000000..11260159e3bd --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_clock.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M11.99,22C17.52,22,22,17.52,22,12c0-5.52-4.48-10-10.01-10C6.47,2,2,6.48,2,12C2,17.52,6.47,22,11.99,22z M11,6.82 c0-0.4,0.25-0.72,0.75-0.72s0.75,0.32,0.75,0.72v5.43l3.87,2.3c0.01,0,0.01,0.01,0.02,0.01c0.33,0.21,0.44,0.64,0.23,0.98 c-0.2,0.34-0.64,0.44-0.98,0.24L11,13V6.82z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_grid.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_grid.xml new file mode 100644 index 000000000000..0397b6c3744c --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_grid.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M22,7c0.55,0,1-0.45,1-1s-0.45-1-1-1h-3V2c0-0.55-0.45-1-1-1s-1,0.45-1,1v3h-4V2c0-0.55-0.45-1-1-1s-1,0.45-1,1v3H7V2 c0-0.55-0.45-1-1-1S5,1.45,5,2v3H2C1.45,5,1,5.45,1,6s0.45,1,1,1h3v4H2c-0.55,0-1,0.45-1,1s0.45,1,1,1h3v4H2c-0.55,0-1,0.45-1,1 s0.45,1,1,1h3v3c0,0.55,0.45,1,1,1s1-0.45,1-1v-3h4v3c0,0.55,0.45,1,1,1s1-0.45,1-1v-3h4v3c0,0.55,0.45,1,1,1s1-0.45,1-1v-3h3 c0.55,0,1-0.45,1-1s-0.45-1-1-1h-3v-4h3c0.55,0,1-0.45,1-1s-0.45-1-1-1h-3V7H22z M11,17H7v-4h4V17z M11,11H7V7h4V11z M17,17h-4v-4 h4V17z M17,11h-4V7h4V11z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_theme.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_theme.xml new file mode 100644 index 000000000000..6f0462cd9932 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_theme.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M18,2H6C5.45,2,5,2.45,5,3v9c0,1.65,1.35,3,3,3h2v6c0,0.55,0.45,1,1,1h2c0.55,0,1-0.45,1-1v-6h2c1.65,0,3-1.35,3-3V3 C19,2.45,18.55,2,18,2z M17,9H7V4h2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V4h2v2c0,0.55,0.45,1,1,1c0.55,0,1-0.45,1-1V4h2V9z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml new file mode 100644 index 000000000000..ea195ca209e7 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M 15.5 7 C 16.3284271247 7 17 7.67157287525 17 8.5 C 17 9.32842712475 16.3284271247 10 15.5 10 C 14.6715728753 10 14 9.32842712475 14 8.5 C 14 7.67157287525 14.6715728753 7 15.5 7 Z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M3.5,11h1C4.78,11,5,10.78,5,10.5V5h5.5C10.78,5,11,4.78,11,4.5v-1C11,3.22,10.78,3,10.5,3H5C3.9,3,3,3.9,3,5v5.5 C3,10.78,3.22,11,3.5,11z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M19,3h-5.5C13.22,3,13,3.22,13,3.5v1C13,4.78,13.22,5,13.5,5H19v5.5c0,0.28,0.22,0.5,0.5,0.5h1c0.28,0,0.5-0.22,0.5-0.5V5 C21,3.9,20.1,3,19,3z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.5,13h-1c-0.28,0-0.5,0.22-0.5,0.5V19h-5.5c-0.28,0-0.5,0.22-0.5,0.5v1c0,0.28,0.22,0.5,0.5,0.5H19c1.1,0,2-0.9,2-2 v-5.5C21,13.22,20.78,13,20.5,13z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M10.5,19H5v-5.5C5,13.22,4.78,13,4.5,13h-1C3.22,13,3,13.22,3,13.5V19c0,1.1,0.9,2,2,2h5.5c0.28,0,0.5-0.22,0.5-0.5v-1 C11,19.22,10.78,19,10.5,19z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M10.78,12.98c-0.4-0.5-1.16-0.5-1.56,0l-2.57,3.21C6.39,16.51,6.62,17,7.04,17H17c0.41,0,0.65-0.47,0.4-0.8l-1.6-2.13 c-0.4-0.53-1.2-0.53-1.6,0l-1.23,1.64L10.78,12.98z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_shapes_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_shapes_24px.xml new file mode 100644 index 000000000000..cea09b56dd1d --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_shapes_24px.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M20,9h-3v2h3v9H10v-2H8v2c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2v-9C22,9.9,21.1,9,20,9z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M15,9c0-1.07-0.25-2.09-0.68-3C13.2,3.64,10.79,2,8,2C4.13,2,1,5.13,1,9c0,2.79,1.64,5.2,4,6.32C5.91,15.75,6.93,16,8,16 C11.87,16,15,12.87,15,9z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_tune.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_tune.xml new file mode 100644 index 000000000000..ae03b51f7010 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_tune.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="20dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="20dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M8,9c-0.55,0-1,0.45-1,1v1H4c-0.55,0-1,0.45-1,1c0,0.55,0.45,1,1,1h3v1c0,0.55,0.45,1,1,1s1-0.45,1-1v-4 C9,9.45,8.55,9,8,9z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20,5h-3V4c0-0.55-0.45-1-1-1s-1,0.45-1,1v4c0,0.55,0.45,1,1,1s1-0.45,1-1V7h3c0.55,0,1-0.45,1-1C21,5.45,20.55,5,20,5z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20,11h-9v2h9c0.55,0,1-0.45,1-1C21,11.45,20.55,11,20,11z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20,17h-7v-1c0-0.55-0.45-1-1-1s-1,0.45-1,1v4c0,0.55,0.45,1,1,1s1-0.45,1-1v-1h7c0.55,0,1-0.45,1-1S20.55,17,20,17z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M4,7h9V5H4C3.45,5,3,5.45,3,6C3,6.55,3.45,7,4,7z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M3,18c0,0.55,0.45,1,1,1h5v-2H4C3.45,17,3,17.45,3,18z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_wifi_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_wifi_24px.xml new file mode 100644 index 000000000000..03e142e2be35 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_wifi_24px.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M11.29,19.29c0.39,0.39,1.03,0.4,1.42,0L14,18c0.47-0.47,0.38-1.28-0.22-1.58C13.25,16.15,12.64,16,12,16 c-0.64,0-1.24,0.15-1.77,0.41c-0.59,0.29-0.69,1.11-0.22,1.58L11.29,19.29z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M17.6,14.39l0.71-0.71c0.42-0.42,0.39-1.12-0.08-1.5C16.52,10.82,14.35,10,12,10c-2.34,0-4.5,0.81-6.21,2.17 c-0.47,0.37-0.51,1.07-0.09,1.49l0.71,0.71c0.35,0.36,0.92,0.39,1.32,0.08C8.91,13.54,10.39,13,12,13c1.61,0,3.1,0.55,4.29,1.47 C16.69,14.78,17.25,14.75,17.6,14.39z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M21.83,10.16l0.71-0.71c0.42-0.42,0.38-1.09-0.06-1.48C19.68,5.5,16.01,4,12,4C8.01,4,4.36,5.49,1.56,7.94 C1.12,8.33,1.08,9,1.49,9.41l0.71,0.71c0.37,0.37,0.96,0.4,1.35,0.06C5.81,8.2,8.77,7,12,7c3.25,0,6.22,1.22,8.49,3.22 C20.88,10.56,21.47,10.53,21.83,10.16z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml new file mode 100644 index 000000000000..09e7360069c4 --- /dev/null +++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_bluetooth_transient_animation_drawable"> + <target android:name="_R_G_L_1_G_D_0_P_0" + android:animation="@*android:anim/ic_bluetooth_transient_animation_0"/> + <target android:name="_R_G_L_0_G_D_0_P_0" + android:animation="@*android:anim/ic_bluetooth_transient_animation_1"/> + <target android:name="time_group" + android:animation="@*android:anim/ic_bluetooth_transient_animation_2"/> +</animated-vector> diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml new file mode 100644 index 000000000000..e0f155a013ef --- /dev/null +++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" + android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_2_G" android:translateX="3.75" + android:translateY="1.4410000000000007"> + <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M13.78 5.28 C13.78,5.28 9.03,0.53 9.03,0.53 C8.82,0.31 8.49,0.25 8.21,0.37 C7.93,0.48 7.75,0.75 7.75,1.06 C7.75,1.06 7.75,8.75 7.75,8.75 C7.75,8.75 3.78,4.78 3.78,4.78 C3.49,4.49 3.01,4.49 2.72,4.78 C2.43,5.07 2.43,5.55 2.72,5.84 C2.72,5.84 7.44,10.56 7.44,10.56 C7.44,10.56 2.72,15.28 2.72,15.28 C2.43,15.57 2.43,16.05 2.72,16.34 C3.01,16.63 3.49,16.63 3.78,16.34 C3.78,16.34 7.75,12.37 7.75,12.37 C7.75,12.37 7.75,20.06 7.75,20.06 C7.75,20.36 7.93,20.64 8.21,20.75 C8.31,20.79 8.4,20.81 8.5,20.81 C8.7,20.81 8.89,20.73 9.03,20.59 C9.03,20.59 13.78,15.84 13.78,15.84 C14.07,15.55 14.07,15.07 13.78,14.78 C13.78,14.78 9.56,10.56 9.56,10.56 C9.56,10.56 13.78,6.34 13.78,6.34 C14.07,6.05 14.07,5.57 13.78,5.28c M12.19 15.31 C12.19,15.31 9.25,18.25 9.25,18.25 C9.25,18.25 9.25,12.37 9.25,12.37 C9.25,12.37 12.19,15.31 12.19,15.31c M9.25 8.75 C9.25,8.75 9.25,2.87 9.25,2.87 C9.25,2.87 12.19,5.81 12.19,5.81 C12.19,5.81 9.25,8.75 9.25,8.75c "/> + </group> + <group android:name="_R_G_L_1_G" android:translateX="3.75" + android:translateY="1.4410000000000007"> + <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M15.25 9.56 C14.7,9.56 14.25,10.01 14.25,10.56 C14.25,11.11 14.7,11.56 15.25,11.56 C15.8,11.56 16.25,11.11 16.25,10.56 C16.25,10.01 15.8,9.56 15.25,9.56c "/> + </group> + <group android:name="_R_G_L_0_G" android:translateX="3.75" + android:translateY="1.4410000000000007"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M1.25 9.56 C0.7,9.56 0.25,10.01 0.25,10.56 C0.25,11.11 0.7,11.56 1.25,11.56 C1.8,11.56 2.25,11.11 2.25,10.56 C2.25,10.01 1.8,9.56 1.25,9.56c "/> + </group> + </group> + <group android:name="time_group"/> +</vector> +
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml new file mode 100644 index 000000000000..1317f66b4131 --- /dev/null +++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_hotspot_transient_animation_drawable"> + <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_0"/> + <target android:name="_R_G_L_0_G_D_1_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_1"/> + <target android:name="_R_G_L_0_G_D_2_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_2"/> + <target android:name="time_group" android:animation="@*android:anim/ic_hotspot_transient_animation_3"/> +</animated-vector> diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml new file mode 100644 index 000000000000..6b87f63eb193 --- /dev/null +++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" + android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_0_G" android:translateX="2" + android:translateY="1.552999999999999"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M8.5 10.95 C8.5,11.77 9.17,12.45 10,12.45 C10.83,12.45 11.5,11.77 11.5,10.95 C11.5,10.12 10.83,9.45 10,9.45 C9.17,9.45 8.5,10.12 8.5,10.95c "/> + <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M13.01 15.01 C13.3,15.31 13.77,15.31 14.07,15.01 C15.15,13.93 15.75,12.48 15.75,10.95 C15.75,9.41 15.15,7.97 14.07,6.88 C11.82,4.64 8.18,4.64 5.94,6.88 C4.85,7.97 4.25,9.41 4.25,10.95 C4.25,12.48 4.85,13.93 5.94,15.01 C6.08,15.16 6.27,15.23 6.47,15.23 C6.66,15.23 6.85,15.16 6.99,15.01 C7.29,14.72 7.29,14.25 6.99,13.95 C6.19,13.15 5.75,12.08 5.75,10.95 C5.75,9.81 6.19,8.74 6.99,7.94 C8.65,6.28 11.35,6.28 13.01,7.94 C13.81,8.74 14.25,9.81 14.25,10.95 C14.25,12.08 13.81,13.15 13.01,13.95 C12.71,14.25 12.71,14.72 13.01,15.01c "/> + <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M16.36 18.06 C16.56,18.06 16.75,17.99 16.89,17.84 C18.74,16 19.75,13.55 19.75,10.95 C19.75,8.34 18.74,5.89 16.89,4.05 C13.09,0.25 6.91,0.25 3.11,4.05 C1.26,5.89 0.25,8.34 0.25,10.95 C0.25,13.55 1.26,16 3.11,17.84 C3.4,18.13 3.87,18.13 4.17,17.84 C4.46,17.55 4.46,17.07 4.17,16.78 C2.61,15.22 1.75,13.15 1.75,10.95 C1.75,8.74 2.61,6.67 4.17,5.11 C7.38,1.9 12.62,1.9 15.83,5.11 C17.39,6.67 18.25,8.74 18.25,10.95 C18.25,13.15 17.39,15.22 15.83,16.78 C15.54,17.07 15.54,17.55 15.83,17.84 C15.98,17.99 16.17,18.06 16.36,18.06c "/> + </group> + </group> + <group android:name="time_group"/> +</vector> diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml new file mode 100644 index 000000000000..ae64e569f399 --- /dev/null +++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_signal_wifi_transient_animation_drawable"> + <target android:name="_R_G_L_4_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_0"/> + <target android:name="_R_G_L_3_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_1"/> + <target android:name="_R_G_L_3_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_2"/> + <target android:name="_R_G_L_2_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_3"/> + <target android:name="_R_G_L_2_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_4"/> + <target android:name="_R_G_L_1_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_5"/> + <target android:name="_R_G_L_1_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_6"/> + <target android:name="_R_G_L_0_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_7"/> + <target android:name="time_group" android:animation="@*android:anim/ic_signal_wifi_transient_animation_8"/> +</animated-vector> diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml new file mode 100644 index 000000000000..4a2505adee83 --- /dev/null +++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_4_G" android:translateX="0.10500000000000043" + android:translateY="1.7490000000000006"> + <path android:name="_R_G_L_4_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M11.9 0.25 C7.65,0.25 3.78,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 11.12,19.31 11.12,19.31 C11.32,19.55 11.61,19.67 11.9,19.67 C12.18,19.67 12.47,19.55 12.67,19.31 C12.67,19.31 23.04,6.68 23.04,6.68 C23.54,6.07 23.49,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.9,0.25c M11.9 17.89 C11.9,17.89 1.91,5.73 1.91,5.73 C4.64,3.16 8.18,1.75 11.9,1.75 C15.61,1.75 19.15,3.16 21.88,5.73 C21.88,5.73 11.9,17.89 11.9,17.89c "/> + </group> + <group android:name="_R_G_L_3_G" android:translateX="0.10500000000000043" + android:translateY="1.75"> + <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M11.9 0.25 C7.65,0.25 3.78,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 7.48,14.87 7.48,14.87 C7.48,14.87 7.48,14.87 7.48,14.87 C7.48,14.87 8,15.51 8,15.51 C8,15.51 11.12,19.31 11.12,19.31 C11.32,19.55 11.61,19.67 11.9,19.67 C12.18,19.67 12.47,19.55 12.67,19.31 C12.67,19.31 15.79,15.51 15.79,15.51 C15.79,15.51 16.31,14.87 16.31,14.87 C16.31,14.87 16.31,14.87 16.31,14.87 C16.31,14.87 23.04,6.68 23.04,6.68 C23.54,6.07 23.49,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.9,0.25c M15.67 13.29 C15.57,13.15 15.47,13.01 15.36,12.88 C14.54,11.88 13.3,11.24 11.9,11.24 C10.5,11.24 9.25,11.88 8.43,12.88 C8.32,13.01 8.22,13.15 8.12,13.29 C8.12,13.29 1.91,5.73 1.91,5.73 C4.64,3.16 8.18,1.75 11.9,1.75 C15.61,1.75 19.15,3.16 21.88,5.73 C21.88,5.73 15.67,13.29 15.67,13.29c "/> + </group> + <group android:name="_R_G_L_2_G" android:translateX="0.10500000000000043" + android:translateY="1.7490000000000006"> + <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M11.89 0.25 C7.65,0.25 3.77,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 11.12,19.31 11.12,19.31 C11.32,19.55 11.61,19.67 11.89,19.67 C12.18,19.67 12.47,19.55 12.67,19.31 C12.67,19.31 23.04,6.68 23.04,6.68 C23.54,6.07 23.48,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.89,0.25c M17.61 10.93 C17.5,10.8 17.4,10.66 17.28,10.54 C15.92,9.12 14.01,8.24 11.89,8.24 C9.77,8.24 7.86,9.12 6.51,10.54 C6.39,10.66 6.29,10.8 6.18,10.92 C6.18,10.92 1.91,5.73 1.91,5.73 C4.64,3.16 8.18,1.75 11.89,1.75 C15.6,1.75 19.15,3.16 21.88,5.73 C21.88,5.73 17.61,10.93 17.61,10.93c "/> + </group> + <group android:name="_R_G_L_1_G" android:translateX="0.10500000000000043" + android:translateY="1.7490000000000006"> + <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M11.9 0.25 C7.65,0.25 3.78,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 3.2,9.66 3.2,9.66 C3.2,9.66 3.2,9.66 3.2,9.66 C3.2,9.66 11.12,19.31 11.12,19.31 C11.52,19.8 12.27,19.8 12.67,19.31 C12.67,19.31 20.6,9.66 20.6,9.66 C20.6,9.66 20.59,9.66 20.59,9.66 C20.59,9.66 23.04,6.68 23.04,6.68 C23.54,6.07 23.49,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.9,0.25c M19.6 8.5 C19.51,8.41 19.43,8.32 19.34,8.23 C17.41,6.37 14.8,5.24 11.9,5.24 C8.99,5.24 6.38,6.38 4.45,8.23 C4.36,8.32 4.28,8.41 4.19,8.51 C4.19,8.51 1.91,5.73 1.91,5.73 C4.64,3.16 8.18,1.75 11.9,1.75 C15.61,1.75 19.15,3.16 21.88,5.73 C21.88,5.73 19.6,8.5 19.6,8.5c "/> + </group> + <group android:name="_R_G_L_0_G" android:translateX="0.10500000000000043" + android:translateY="1.75"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M11.9 0.25 C7.65,0.25 3.78,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 11.12,19.31 11.12,19.31 C11.52,19.8 12.27,19.8 12.67,19.31 C12.67,19.31 23.04,6.68 23.04,6.68 C23.54,6.07 23.49,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.9,0.25c "/> + </group> + </group> + <group android:name="time_group"/> +</vector> diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/values/config.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/values/config.xml new file mode 100644 index 000000000000..ebcac82c695f --- /dev/null +++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/values/config.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2019, 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. + */ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="config_batterymeterPerimeterPath" translatable="false"> + M 11,1.505 H 8 V 0.995 C 8,0.445 7.55,-0.005 7,-0.005 H 5 C 4.45,-0.005 4,0.445 4,0.995 V 1.505 H 1 C 0.45,1.505 0,1.955 0,2.505 V 19.005 C 0,19.555 0.45,20.005 1,20.005 H 11 C 11.55,20.005 12,19.555 12,19.005 V 2.505 C 12,1.955 11.543,1.505 11,1.505 Z M 10.5,18.505 H 1.5 V 3.005 H 10.5 Z + </string> + + <string name="config_batterymeterFillMask" translatable="false"> + M 10.5,18.505 H 1.5 V 3.005 H 10.5 Z + </string> + <string name="config_batterymeterBoltPath" translatable="false"> + M 3.92,11.5 H 5 V 15.01 C 5,15.17 5.13,15.26 5.25,15.26 5.33,15.26 5.42,15.22 5.47,15.13 L 8.3,9.87 C 8.39,9.7 8.27,9.5 8.08,9.5 H 7 V 5.99 C 7,5.83 6.87,5.74 6.75,5.74 6.67,5.74 6.58,5.78 6.53,5.87 L 3.7,11.13 C 3.61,11.3 3.73,11.5 3.92,11.5 Z + </string> + <string name="config_batterymeterPowersavePath" translatable="false"> + M 3.75,11.25 H 5.25 V 12.75 C 5.25,13.16 5.59,13.5 6,13.5 6.41,13.5 6.75,13.16 6.75,12.75 V 11.25 H 8.25 C 8.66,11.25 9,10.91 9,10.5 9,10.09 8.66,9.7499 8.25,9.7499 H 6.75 V 8.2499 C 6.75,7.8399 6.41,7.4999 6,7.4999 5.59,7.4999 5.2794,7.841 5.25,8.2499 V 9.7499 H 3.75 C 3.34,9.7499 3,10.09 3,10.5 3,10.91 3.3401,11.25 3.75,11.25 Z + </string> +</resources> diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/Android.mk b/packages/overlays/IconPackRoundedThemePickerOverlay/Android.mk new file mode 100644 index 000000000000..ae48186e05ee --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/Android.mk @@ -0,0 +1,31 @@ +# +# Copyright 2019, 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. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_RRO_THEME := IconPackRoundedThemePicker +LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := IconPackRoundedThemePickerOverlay +LOCAL_SDK_VERSION := current + +include $(BUILD_RRO_PACKAGE) diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/AndroidManifest.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..9a90a05713fe --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/AndroidManifest.xml @@ -0,0 +1,24 @@ +<!-- +/** + * Copyright (c) 2019, 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. + */ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.theme.icon_pack.rounded.themepicker" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="com.android.wallpaper" android:category="android.theme.customization.icon_pack.themepicker" android:priority="1"/> + <application android:label="Rounded" android:hasCode="false"/> +</manifest> diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_add_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_add_24px.xml new file mode 100644 index 000000000000..707369ab3446 --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_add_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M4.67,12.75h6.58v6.58C11.25,19.7,11.59,20,12,20s0.75-0.3,0.75-0.67v-6.58h6.58C19.7,12.75,20,12.41,20,12 s-0.3-0.75-0.67-0.75h-6.58V4.67C12.75,4.3,12.41,4,12,4s-0.75,0.3-0.75,0.67v6.58H4.67C4.3,11.25,4,11.59,4,12 S4.3,12.75,4.67,12.75z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_close_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_close_24px.xml new file mode 100644 index 000000000000..1dca14d9de77 --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_close_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M18.78,5.22c-0.29-0.29-0.77-0.29-1.06,0L12,10.94L6.28,5.22c-0.29-0.29-0.77-0.29-1.06,0s-0.29,0.77,0,1.06L10.94,12 l-5.72,5.72c-0.29,0.29-0.29,0.77,0,1.06C5.37,18.93,5.56,19,5.75,19s0.38-0.07,0.53-0.22L12,13.06l5.72,5.72 c0.15,0.15,0.34,0.22,0.53,0.22s0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06L13.06,12l5.72-5.72 C19.07,5.99,19.07,5.51,18.78,5.22z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_colorize_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_colorize_24px.xml new file mode 100644 index 000000000000..5c21b23f0bc7 --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_colorize_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M11.47,4.47c-0.29,0.29-0.29,0.77,0,1.06l1.5,1.5l-9.68,9.68C3.11,16.89,3,17.15,3,17.42l0,2.58c0,0.55,0.45,1,1,1L6.58,21 c0,0,0,0,0,0c0.27,0,0.52-0.11,0.71-0.29l9.68-9.68l1.5,1.5c0.15,0.15,0.34,0.22,0.53,0.22c0.19,0,0.38-0.07,0.53-0.22 c0.29-0.29,0.29-0.77,0-1.06l-2.09-2.09l2.97-2.97c0.78-0.78,0.78-2.05,0-2.83c-0.78-0.78-2.05-0.78-2.83,0l-2.97,2.97l-2.09-2.09 C12.24,4.17,11.77,4.17,11.47,4.47z M6.38,19.5L4.5,19.49l0-1.87l9-9l1.88,1.88L6.38,19.5z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_delete_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_delete_24px.xml new file mode 100644 index 000000000000..48a430fc420b --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_delete_24px.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M20,4h-1h-4c0-0.55-0.45-1-1-1h-4C9.45,3,9,3.45,9,4H5H4C3.59,4,3.25,4.34,3.25,4.75S3.59,5.5,4,5.5h1V18 c0,1.66,1.34,3,3,3h8c1.66,0,3-1.34,3-3V5.5h1c0.41,0,0.75-0.34,0.75-0.75S20.41,4,20,4z M17.5,18c0,0.83-0.67,1.5-1.5,1.5H8 c-0.83,0-1.5-0.67-1.5-1.5V5.5h11V18z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M14.25,8c-0.41,0-0.75,0.34-0.75,0.75v7.5c0,0.41,0.34,0.75,0.75,0.75S15,16.66,15,16.25v-7.5C15,8.34,14.66,8,14.25,8z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M9.75,8C9.34,8,9,8.34,9,8.75v7.5C9,16.66,9.34,17,9.75,17s0.75-0.34,0.75-0.75v-7.5C10.5,8.34,10.16,8,9.75,8z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_font.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_font.xml new file mode 100644 index 000000000000..bbae929cf76e --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_font.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M20,2H4C2.9,2,2,2.9,2,4v16c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M20.5,20c0,0.27-0.23,0.5-0.5,0.5H4 c-0.27,0-0.5-0.23-0.5-0.5V4c0-0.27,0.23-0.5,0.5-0.5h16c0.27,0,0.5,0.23,0.5,0.5V20z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M10.69,6L6.2,18h2.5l1-2.87h4.59L15.3,18h2.5L13.29,6H10.69z M10.43,13.06l1.51-4.46h0.13l1.49,4.46H10.43z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_clock.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_clock.xml new file mode 100644 index 000000000000..9c9d663f850c --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_clock.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M19.07,4.93c-3.91-3.9-10.24-3.9-14.14,0.01s-3.9,10.24,0.01,14.14s10.24,3.9,14.14-0.01C20.95,17.2,22,14.65,22,12 C22,9.35,20.94,6.81,19.07,4.93z M18,18c-1.6,1.59-3.76,2.48-6.02,2.48c-4.69-0.01-8.49-3.83-8.48-8.52 c0.01-4.69,3.83-8.49,8.52-8.48c4.69,0.01,8.49,3.83,8.48,8.52C20.49,14.25,19.6,16.41,18,18z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M12.75,11.69V6.75C12.75,6.34,12.41,6,12,6s-0.75,0.34-0.75,0.75V12c0,0.2,0.08,0.39,0.22,0.53l3.25,3.25 c0.15,0.15,0.34,0.22,0.53,0.22s0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06L12.75,11.69z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_grid.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_grid.xml new file mode 100644 index 000000000000..c81ca1e4bde1 --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_grid.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,7C20.66,7,21,6.66,21,6.25S20.66,5.5,20.25,5.5H18.5V3.75C18.5,3.34,18.16,3,17.75,3S17,3.34,17,3.75V5.5h-4.25 V3.75C12.75,3.34,12.41,3,12,3s-0.75,0.34-0.75,0.75V5.5H7V3.75C7,3.34,6.66,3,6.25,3S5.5,3.34,5.5,3.75V5.5H3.75 C3.34,5.5,3,5.84,3,6.25S3.34,7,3.75,7H5.5v4.25H3.75C3.34,11.25,3,11.59,3,12s0.34,0.75,0.75,0.75H5.5V17H3.75 C3.34,17,3,17.34,3,17.75s0.34,0.75,0.75,0.75H5.5v1.75C5.5,20.66,5.84,21,6.25,21S7,20.66,7,20.25V18.5h4.25v1.75 c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75V18.5H17v1.75c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75V18.5h1.75 c0.41,0,0.75-0.34,0.75-0.75S20.66,17,20.25,17H18.5v-4.25h1.75c0.41,0,0.75-0.34,0.75-0.75s-0.34-0.75-0.75-0.75H18.5V7H20.25z M7,7h4.25v4.25H7V7z M7,17v-4.25h4.25V17H7z M17,17h-4.25v-4.25H17V17z M17,11.25h-4.25V7H17V11.25z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_theme.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_theme.xml new file mode 100644 index 000000000000..32d154b0a8ea --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_theme.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M11.5,22h1c1.1,0,2-0.9,2-2v-6H16c1.66,0,3-1.34,3-3V3c0-0.55-0.45-1-1-1H6C5.45,2,5,2.45,5,3v8c0,1.66,1.34,3,3,3h1.5v6 C9.5,21.1,10.4,22,11.5,22z M9,3.5v2.75C9,6.66,9.34,7,9.75,7s0.75-0.34,0.75-0.75V3.5h3v2.75C13.5,6.66,13.84,7,14.25,7 S15,6.66,15,6.25V3.5h2.5V9h-11V3.5H9z M8,12.5c-0.83,0-1.5-0.67-1.5-1.5v-0.5h11V11c0,0.83-0.67,1.5-1.5,1.5h-3V14v6 c0,0.28-0.22,0.5-0.5,0.5h-1c-0.28,0-0.5-0.22-0.5-0.5v-6v-1.5H8z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml new file mode 100644 index 000000000000..21daf9d00e89 --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M 16 6.75 C 16.6903559373 6.75 17.25 7.30964406271 17.25 8 C 17.25 8.69035593729 16.6903559373 9.25 16 9.25 C 15.3096440627 9.25 14.75 8.69035593729 14.75 8 C 14.75 7.30964406271 15.3096440627 6.75 16 6.75 Z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M3.75,10.75c0.41,0,0.75-0.34,0.75-0.75V4.75c0-0.14,0.11-0.25,0.25-0.25H10c0.41,0,0.75-0.34,0.75-0.75S10.41,3,10,3 H4.75C3.79,3,3,3.79,3,4.75V10C3,10.41,3.34,10.75,3.75,10.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M10,19.5H4.75c-0.14,0-0.25-0.11-0.25-0.25V14c0-0.41-0.34-0.75-0.75-0.75S3,13.59,3,14v5.25C3,20.21,3.79,21,4.75,21H10 c0.41,0,0.75-0.34,0.75-0.75S10.41,19.5,10,19.5z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,13.25c-0.41,0-0.75,0.34-0.75,0.75v5.25c0,0.14-0.11,0.25-0.25,0.25H14c-0.41,0-0.75,0.34-0.75,0.75 S13.59,21,14,21h5.25c0.96,0,1.75-0.79,1.75-1.75V14C21,13.59,20.66,13.25,20.25,13.25z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M19.25,3H14c-0.41,0-0.75,0.34-0.75,0.75S13.59,4.5,14,4.5h5.25c0.14,0,0.25,0.11,0.25,0.25V10 c0,0.41,0.34,0.75,0.75,0.75S21,10.41,21,10V4.75C21,3.79,20.21,3,19.25,3z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M13.73,12.37l-2.6,3.35l-1.74-2.1c-0.2-0.25-0.58-0.24-0.78,0.01l-1.99,2.56C6.36,16.52,6.59,17,7.01,17h9.98 c0.41,0,0.65-0.47,0.4-0.8l-2.87-3.83C14.32,12.11,13.93,12.11,13.73,12.37z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_shapes_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_shapes_24px.xml new file mode 100644 index 000000000000..19ce4e3ca7ad --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_shapes_24px.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M20,9h-2.51c0,0.1,0.01,0.2,0.01,0.3v1.2H20c0.28,0,0.5,0.22,0.5,0.5v9c0,0.28-0.22,0.5-0.5,0.5H10 c-0.28,0-0.5-0.22-0.5-0.5v-2.5H8V20c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2v-9C22,9.9,21.1,9,20,9z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M16,9c0-3.87-3.13-7-7-7C5.13,2,2,5.13,2,9c0,3.87,3.13,7,7,7C12.87,16,16,12.87,16,9z M3.5,9c0-3.03,2.47-5.5,5.5-5.5 s5.5,2.47,5.5,5.5s-2.47,5.5-5.5,5.5S3.5,12.03,3.5,9z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_tune.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_tune.xml new file mode 100644 index 000000000000..2a56cc54bae8 --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_tune.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="20dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="20dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M3.75,4.75C3.34,4.75,3,5.09,3,5.5s0.34,0.75,0.75,0.75H14v-1.5H3.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,4.75H17v-1.5c0-0.41-0.34-0.75-0.75-0.75S15.5,2.84,15.5,3.25v4.5c0,0.41,0.34,0.75,0.75,0.75S17,8.16,17,7.75v-1.5 h3.25C20.66,6.25,21,5.91,21,5.5S20.66,4.75,20.25,4.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,11.25H10v1.5h10.25c0.41,0,0.75-0.34,0.75-0.75S20.66,11.25,20.25,11.25z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M3.75,17.75C3.34,17.75,3,18.09,3,18.5s0.34,0.75,0.75,0.75H10v-1.5H3.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,17.75H13v-1.5c0-0.41-0.34-0.75-0.75-0.75s-0.75,0.34-0.75,0.75v4.5c0,0.41,0.34,0.75,0.75,0.75S13,21.16,13,20.75 v-1.5h7.25c0.41,0,0.75-0.34,0.75-0.75S20.66,17.75,20.25,17.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M8.5,9.75C8.5,9.34,8.16,9,7.75,9S7,9.34,7,9.75v1.5H3.75C3.34,11.25,3,11.59,3,12s0.34,0.75,0.75,0.75H7v1.5 C7,14.66,7.34,15,7.75,15s0.75-0.34,0.75-0.75V9.75z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_wifi_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_wifi_24px.xml new file mode 100644 index 000000000000..0a1c3055870d --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_wifi_24px.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M12,2.75C7.95,2.69,4.05,4.3,1.22,7.2C0.96,7.5,0.97,7.95,1.24,8.23C1.53,8.53,2,8.54,2.3,8.25c2.55-2.61,6.05-4.06,9.7-4 c3.65-0.06,7.17,1.4,9.72,4.02c0.28,0.27,0.73,0.28,1.03,0.01c0.31-0.28,0.33-0.75,0.05-1.06C19.96,4.32,16.06,2.69,12,2.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M15.78,14.82c0.05,0.06,0.1,0.11,0.17,0.15c0.34,0.23,0.81,0.14,1.04-0.21s0.14-0.81-0.21-1.04 c-2.64-2.64-6.91-2.64-9.55,0c-0.27,0.29-0.27,0.73,0,1.02c0.28,0.3,0.76,0.32,1.06,0.04h0.03c0,0,0,0,0.01-0.01 c2.05-2.05,5.37-2.04,7.42,0.01C15.75,14.8,15.76,14.81,15.78,14.82z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M 12 17 C 12.8284271247 17 13.5 17.6715728753 13.5 18.5 C 13.5 19.3284271247 12.8284271247 20 12 20 C 11.1715728753 20 10.5 19.3284271247 10.5 18.5 C 10.5 17.6715728753 11.1715728753 17 12 17 Z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.03,11.79c0.3-0.29,0.3-0.77,0.01-1.06h-0.01c-2.12-2.18-5.01-3.44-8.04-3.5c-3.04,0.06-5.93,1.32-8.05,3.5 c-0.29,0.3-0.28,0.77,0.01,1.06c0.3,0.29,0.77,0.28,1.06-0.01c1.85-1.88,4.36-2.96,7-3c2.62,0.05,5.11,1.13,6.95,3 C19.25,12.07,19.73,12.08,20.03,11.79z" /> +</vector>
\ No newline at end of file diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 21c6035da038..fe92d45b297d 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -7206,6 +7206,13 @@ message MetricsEvent { // ACTION: Share Wi-Fi hotspot by generating a QR code ACTION_SETTINGS_SHARE_WIFI_HOTSPOT_QR_CODE = 1712; + // OPEN: Settings > Network & internet > Mobile network > Delete sim + DIALOG_DELETE_SIM_CONFIRMATION = 1713; + + // OPEN: Settings > Network & internet > Mobile network > Delete sim > (answer yes to + // confirmation) + DIALOG_DELETE_SIM_PROGRESS = 1714; + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java index 3d392c70a1e7..80b0375a229d 100644 --- a/services/autofill/java/com/android/server/autofill/Helper.java +++ b/services/autofill/java/com/android/server/autofill/Helper.java @@ -211,25 +211,26 @@ public final class Helper { * Gets the {@link AutofillId} of the autofillable nodes in the {@code structure}. */ @NonNull - static ArraySet<AutofillId> getAutofillableIds(@NonNull AssistStructure structure) { - final ArraySet<AutofillId> ids = new ArraySet<>(); + static ArrayList<AutofillId> getAutofillIds(@NonNull AssistStructure structure, + boolean autofillableOnly) { + final ArrayList<AutofillId> ids = new ArrayList<>(); final int size = structure.getWindowNodeCount(); for (int i = 0; i < size; i++) { final WindowNode node = structure.getWindowNodeAt(i); - addAutofillableIds(node.getRootViewNode(), ids); + addAutofillableIds(node.getRootViewNode(), ids, autofillableOnly); } return ids; } private static void addAutofillableIds(@NonNull ViewNode node, - @NonNull ArraySet<AutofillId> ids) { - if (node.getAutofillType() != View.AUTOFILL_TYPE_NONE) { + @NonNull ArrayList<AutofillId> ids, boolean autofillableOnly) { + if (!autofillableOnly || node.getAutofillType() != View.AUTOFILL_TYPE_NONE) { ids.add(node.getAutofillId()); } final int size = node.getChildCount(); for (int i = 0; i < size; i++) { final ViewNode child = node.getChildAt(i); - addAutofillableIds(child, ids); + addAutofillableIds(child, ids, autofillableOnly); } } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 7b973536b3bd..f35e464634e0 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -26,7 +26,6 @@ import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED; import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED; import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM; import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString; -import static android.view.autofill.Helper.toList; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static com.android.server.autofill.Helper.getNumericValue; @@ -283,7 +282,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * on autofilling the app. */ @GuardedBy("mLock") - private ArraySet<AutofillId> mAugmentedAutofillableIds; + private ArrayList<AutofillId> mAugmentedAutofillableIds; /** * When {@code true}, the session was created only to handle Augmented Autofill requests (i.e., @@ -336,6 +335,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } + final ArrayList<AutofillId> ids = Helper.getAutofillIds(structure, + /* autofillableOnly= */false); + for (int i = 0; i < ids.size(); i++) { + ids.get(i).setSessionId(Session.this.id); + } + // Flags used to start the session. int flags = structure.getFlags(); @@ -2259,6 +2264,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + id + " destroyed"); return; } + id.setSessionId(this.id); if (sVerbose) { Slog.v(TAG, "updateLocked(" + this.id + "): id=" + id + ", action=" + actionAsString(action) + ", flags=" + flags); @@ -2528,14 +2534,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } private void notifyUnavailableToClient(int sessionFinishedState, - @Nullable ArraySet<AutofillId> autofillableIds) { + @Nullable ArrayList<AutofillId> autofillableIds) { synchronized (mLock) { if (mCurrentViewId == null) return; try { if (mHasCallback) { mClient.notifyNoFillUi(id, mCurrentViewId, sessionFinishedState); } else if (sessionFinishedState != 0) { - mClient.setSessionFinished(sessionFinishedState, toList(autofillableIds)); + mClient.setSessionFinished(sessionFinishedState, autofillableIds); } } catch (RemoteException e) { Slog.e(TAG, "Error notifying client no fill UI: id=" + mCurrentViewId, e); @@ -2661,10 +2667,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final FillContext context = getFillContextByRequestIdLocked(requestId); - final ArraySet<AutofillId> autofillableIds; + final ArrayList<AutofillId> autofillableIds; if (context != null) { final AssistStructure structure = context.getStructure(); - autofillableIds = Helper.getAutofillableIds(structure); + autofillableIds = Helper.getAutofillIds(structure, /* autofillableOnly= */true); } else { Slog.w(TAG, "processNullResponseLocked(): no context for req " + requestId); autofillableIds = null; @@ -2770,7 +2776,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState remoteService.getComponentName().getPackageName()); mAugmentedRequestsLogs.add(log); - remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, mCurrentViewId, + final AutofillId focusedId = AutofillId.withoutSession(mCurrentViewId); + + remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId, currentValue); if (mAugmentedAutofillDestroyer == null) { diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index 843aa741f2c3..73f5cb8326ea 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -297,6 +297,9 @@ final class SaveUi { // First apply the unconditional transformations (if any) to the templates. final ArrayList<Pair<Integer, InternalTransformation>> transformations = customDescription.getTransformations(); + if (sVerbose) { + Slog.v(TAG, "applyCustomDescription(): transformations = " + transformations); + } if (transformations != null) { if (!InternalTransformation.batchApply(valueFinder, template, transformations)) { Slog.w(TAG, "could not apply main transformations on custom description"); @@ -345,6 +348,10 @@ final class SaveUi { // Apply batch updates (if any). final ArrayList<Pair<InternalValidator, BatchUpdates>> updates = customDescription.getUpdates(); + if (sVerbose) { + Slog.v(TAG, "applyCustomDescription(): view = " + customSubtitleView + + " updates=" + updates); + } if (updates != null) { final int size = updates.size(); if (sDebug) Slog.d(TAG, "custom description has " + size + " batch updates"); diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index a7921b5f3892..4399e4267fda 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -161,8 +161,14 @@ final class ContentCapturePerUserService @Override // from PerUserSystemService @GuardedBy("mLock") protected boolean updateLocked(boolean disabled) { - destroyLocked(); final boolean disabledStateChanged = super.updateLocked(disabled); + if (disabledStateChanged) { + // update session content capture enabled state. + for (int i = 0; i < mSessions.size(); i++) { + mSessions.valueAt(i).setContentCaptureEnabledLocked(!disabled); + } + } + destroyLocked(); updateRemoteServiceLocked(disabled); return disabledStateChanged; } @@ -542,7 +548,8 @@ final class ContentCapturePerUserService Slog.v(TAG, "setContentCaptureWhitelist(" + (packages == null ? "null_packages" : packages.size() + " packages") + ", " + (activities == null - ? "null_activities" : activities.size() + " activities") + ")"); + ? "null_activities" : activities.size() + " activities") + ")" + + " for user " + mUserId); } mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities); } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java index d38dfd409439..2643db1d5851 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java @@ -16,6 +16,8 @@ package com.android.server.contentcapture; import static android.service.contentcapture.ContentCaptureService.setClientState; +import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE; +import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_TRUE; import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID; import static android.view.contentcapture.ContentCaptureSession.STATE_ACTIVE; import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED; @@ -24,13 +26,16 @@ import static android.view.contentcapture.ContentCaptureSession.STATE_SERVICE_UP import android.annotation.NonNull; import android.content.ComponentName; +import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteException; import android.service.contentcapture.ContentCaptureService; import android.service.contentcapture.SnapshotData; import android.util.LocalLog; import android.util.Slog; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureSessionId; +import android.view.contentcapture.MainContentCaptureSession; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.IResultReceiver; @@ -108,6 +113,20 @@ final class ContentCaptureServerSession { } /** + * Changes the {@link ContentCaptureService} enabled state. + */ + @GuardedBy("mLock") + public void setContentCaptureEnabledLocked(boolean enabled) { + try { + final Bundle extras = new Bundle(); + extras.putBoolean(MainContentCaptureSession.EXTRA_ENABLED_STATE, true); + mSessionStateReceiver.send(enabled ? RESULT_CODE_TRUE : RESULT_CODE_FALSE, extras); + } catch (RemoteException e) { + Slog.w(TAG, "Error async reporting result to client: " + e); + } + } + + /** * Notifies the {@link ContentCaptureService} of a snapshot of an activity. */ @GuardedBy("mLock") diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java index 9b70272ed952..770931179c5e 100644 --- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java +++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java @@ -28,6 +28,7 @@ import android.app.contentsuggestions.SelectionsRequest; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; +import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.os.Bundle; import android.os.RemoteException; @@ -99,11 +100,17 @@ public final class ContentSuggestionsPerUserService extends ActivityManager.TaskSnapshot snapshot = mActivityTaskManagerInternal.getTaskSnapshot(taskId, false); GraphicBuffer snapshotBuffer = null; + int colorSpaceId = 0; if (snapshot != null) { snapshotBuffer = snapshot.getSnapshot(); + ColorSpace colorSpace = snapshot.getColorSpace(); + if (colorSpace != null) { + colorSpaceId = colorSpace.getId(); + } } - service.provideContextImage(taskId, snapshotBuffer, imageContextRequestExtras); + service.provideContextImage(taskId, snapshotBuffer, colorSpaceId, + imageContextRequestExtras); } } diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java index 4b36352bac51..a8b7b814a9ba 100644 --- a/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java +++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java @@ -68,9 +68,9 @@ public class RemoteContentSuggestionsService extends } void provideContextImage(int taskId, @Nullable GraphicBuffer contextImage, - @NonNull Bundle imageContextRequestExtras) { + int colorSpaceId, @NonNull Bundle imageContextRequestExtras) { scheduleAsyncRequest((s) -> s.provideContextImage(taskId, contextImage, - imageContextRequestExtras)); + colorSpaceId, imageContextRequestExtras)); } void suggestContentSelections( diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 6573c3bf797c..188d65494868 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -1136,11 +1136,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void unbindBluetoothProfileService(int bluetoothProfile, IBluetoothProfileServiceConnection proxy) { synchronized (mProfileServices) { - ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile)); + Integer profile = new Integer(bluetoothProfile); + ProfileServiceConnections psc = mProfileServices.get(profile); if (psc == null) { return; } psc.removeProxy(proxy); + if (psc.isEmpty()) { + // All prxoies are disconnected, unbind with the service. + try { + mContext.unbindService(psc); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e); + } + mProfileServices.remove(profile); + } } } @@ -1298,6 +1308,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mProxies.kill(); } + private boolean isEmpty() { + return mProxies.getRegisteredCallbackCount() == 0; + } + @Override public void onServiceConnected(ComponentName className, IBinder service) { // remove timeout message diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 4658a8dc2828..d52fe8170358 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -120,7 +120,9 @@ import com.android.server.location.MockProvider; import com.android.server.location.PassiveProvider; import com.android.server.location.RemoteListenerHelper; +import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; +import java.io.PrintStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -2299,6 +2301,7 @@ public class LocationManagerService extends ILocationManager.Stub { private boolean mIsForegroundUid; private Location mLastFixBroadcast; private long mLastStatusBroadcast; + private Throwable mStackTrace; // for debugging only /** * Note: must be constructed with lock held. @@ -2311,6 +2314,10 @@ public class LocationManagerService extends ILocationManager.Stub { mIsForegroundUid = isImportanceForeground( mActivityManager.getPackageImportance(mReceiver.mCallerIdentity.mPackageName)); + if (D && receiver.mCallerIdentity.mPid == Process.myPid()) { + mStackTrace = new Throwable(); + } + ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider); if (records == null) { records = new ArrayList<>(); @@ -2361,11 +2368,26 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public String toString() { - return "UpdateRecord[" + mProvider + " " + mReceiver.mCallerIdentity.mPackageName - + "(" + mReceiver.mCallerIdentity.mUid + (mIsForegroundUid ? " foreground" - : " background") - + ")" + " " + mRealRequest + " " - + mReceiver.mWorkSource + "]"; + StringBuilder b = new StringBuilder("UpdateRecord["); + b.append(mProvider).append(" "); + b.append(mReceiver.mCallerIdentity.mPackageName); + b.append("(").append(mReceiver.mCallerIdentity.mUid); + if (mIsForegroundUid) { + b.append(" foreground"); + } else { + b.append(" background"); + } + b.append(") "); + b.append(mRealRequest).append(" ").append(mReceiver.mWorkSource); + + if (mStackTrace != null) { + ByteArrayOutputStream tmp = new ByteArrayOutputStream(); + mStackTrace.printStackTrace(new PrintStream(tmp)); + b.append("\n\n").append(tmp.toString()).append("\n"); + } + + b.append("]"); + return b.toString(); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 11ddceb9b8f2..4d0d3d2dc578 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2185,24 +2185,38 @@ public class ActivityManagerService extends IActivityManager.Stub "hidden_api_access_statslog_sampling_rate"; public void onPropertiesChanged(DeviceConfig.Properties properties) { - int logSampleRate = properties.getInt(HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, 0x0); - if (logSampleRate < 0 || logSampleRate > 0x10000) { - logSampleRate = -1; - } - if (logSampleRate != -1 && logSampleRate != mLogSampleRate) { + int logSampleRate = properties.getInt(HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, + mLogSampleRate); + int statslogSampleRate = properties.getInt(HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE, + mStatslogSampleRate); + setSampleRates(logSampleRate, statslogSampleRate); + } + + private void setSampleRates(int logSampleRate, int statslogSampleRate) { + if (logSampleRate >= 0 && logSampleRate <= 0x10000 + && logSampleRate != mLogSampleRate) { mLogSampleRate = logSampleRate; ZYGOTE_PROCESS.setHiddenApiAccessLogSampleRate(mLogSampleRate); } - int statslogSampleRate = - properties.getInt(HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE, 0); - if (statslogSampleRate < 0 || statslogSampleRate > 0x10000) { - statslogSampleRate = -1; - } - if (statslogSampleRate != -1 && statslogSampleRate != mStatslogSampleRate) { + if (statslogSampleRate >= 0 && statslogSampleRate <= 0x10000 + && statslogSampleRate != mStatslogSampleRate) { mStatslogSampleRate = statslogSampleRate; ZYGOTE_PROCESS.setHiddenApiAccessStatslogSampleRate(mStatslogSampleRate); } + + } + + /** + * Set initial sampling rates from DeviceConfig. This is required after each restart, + * if they never get updated. + */ + private void initializeSampleRates() { + int logSampleRate = DeviceConfig.getInt(DeviceConfig.NAMESPACE_APP_COMPAT, + HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, 0); + int statslogSampleRate = DeviceConfig.getInt(DeviceConfig.NAMESPACE_APP_COMPAT, + HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE, 0); + setSampleRates(logSampleRate, statslogSampleRate); } public HiddenApiSettings(Handler handler, Context context) { @@ -2219,6 +2233,7 @@ public class ActivityManagerService extends IActivityManager.Stub Settings.Global.getUriFor(Settings.Global.HIDDEN_API_POLICY), false, this); + initializeSampleRates(); DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_COMPAT, mContext.getMainExecutor(), this); update(); diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags index 7c2ea3fc57f2..30a297e221ae 100644 --- a/services/core/java/com/android/server/am/EventLogTags.logtags +++ b/services/core/java/com/android/server/am/EventLogTags.logtags @@ -144,3 +144,6 @@ option java_package com.android.server.am 30064 am_on_top_resumed_gained_called (User|1|5),(Component Name|3),(Reason|3) # The activity's onTopResumedActivityChanged(false) has been called. 30065 am_on_top_resumed_lost_called (User|1|5),(Component Name|3),(Reason|3) + +# An activity been add into stopping list +30066 am_add_to_stopping (User|1|5),(Token|1|5),(Component Name|3),(Reason|3)
\ No newline at end of file diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 043daefe0fdb..7abfcea306cc 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -38,6 +38,7 @@ import static android.os.Process.setProcessGroup; import static android.os.Process.setThreadPriority; import static android.os.Process.setThreadScheduler; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ; @@ -60,8 +61,8 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; import android.app.ActivityManager; import android.app.usage.UsageEvents; import android.content.Context; -import android.os.Binder; import android.os.Debug; +import android.os.Handler; import android.os.IBinder; import android.os.PowerManagerInternal; import android.os.Process; @@ -78,6 +79,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.procstats.ProcessStats; import com.android.server.LocalServices; +import com.android.server.ServiceThread; import com.android.server.wm.ActivityServiceConnectionsHolder; import com.android.server.wm.WindowProcessController; @@ -148,6 +150,12 @@ public final class OomAdjuster { /** Track all uids that have actively running processes. */ ActiveUids mActiveUids; + /** + * The handler to execute {@link #setProcessGroup} (it may be heavy if the process has many + * threads) for reducing the time spent in {@link #applyOomAdjLocked}. + */ + private final Handler mProcessGroupHandler; + private final ArraySet<BroadcastQueue> mTmpBroadcastQueue = new ArraySet(); private final ActivityManagerService mService; @@ -161,6 +169,28 @@ public final class OomAdjuster { mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class); mConstants = mService.mConstants; mAppCompact = new AppCompactor(mService); + + // The process group is usually critical to the response time of foreground app, so the + // setter should apply it as soon as possible. + final ServiceThread adjusterThread = new ServiceThread(TAG, TOP_APP_PRIORITY_BOOST, + false /* allowIo */); + adjusterThread.start(); + Process.setThreadGroupAndCpuset(adjusterThread.getThreadId(), THREAD_GROUP_TOP_APP); + mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setProcessGroup"); + final int pid = msg.arg1; + final int group = msg.arg2; + try { + setProcessGroup(pid, group); + } catch (Exception e) { + if (DEBUG_ALL) { + Slog.w(TAG, "Failed setting process group of " + pid + " to " + group, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + return true; + }); } void initSettings() { @@ -1726,9 +1756,9 @@ public final class OomAdjuster { processGroup = THREAD_GROUP_DEFAULT; break; } - long oldId = Binder.clearCallingIdentity(); + mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage( + 0 /* unused */, app.pid, processGroup)); try { - setProcessGroup(app.pid, processGroup); if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { // do nothing if we already switched to RT if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) { @@ -1791,13 +1821,9 @@ public final class OomAdjuster { } } } catch (Exception e) { - if (false) { - Slog.w(TAG, "Failed setting process group of " + app.pid - + " to " + app.getCurrentSchedulingGroup()); - Slog.w(TAG, "at location", e); + if (DEBUG_ALL) { + Slog.w(TAG, "Failed setting thread priority of " + app.pid, e); } - } finally { - Binder.restoreCallingIdentity(oldId); } } } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index c56d8eabf0bb..d04aa8931dca 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -3292,7 +3292,7 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(" Starts a given operation for a particular application."); pw.println(" stop [--user <USER_ID>] <PACKAGE | UID> <OP> "); pw.println(" Stops a given operation for a particular application."); - pw.println(" set [--user <USER_ID>] <--uid PACKAGE | PACKAGE | UID> <OP> <MODE>"); + pw.println(" set [--user <USER_ID>] <[--uid] PACKAGE | UID> <OP> <MODE>"); pw.println(" Set the mode for a particular application and operation."); pw.println(" get [--user <USER_ID>] <PACKAGE | UID> [<OP>]"); pw.println(" Return the mode for a particular application and optional operation."); @@ -3305,12 +3305,11 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(" read-settings"); pw.println(" Read the last written settings, replacing current state in RAM."); pw.println(" options:"); - pw.println(" <PACKAGE> an Android package name."); + pw.println(" <PACKAGE> an Android package name or its UID if prefixed by --uid"); pw.println(" <OP> an AppOps operation."); pw.println(" <MODE> one of allow, ignore, deny, or default"); pw.println(" <USER_ID> the user id under which the package is installed. If --user is not"); pw.println(" specified, the current user is assumed."); - pw.println(" --uid PACKAGE refer to the UID of the package"); } static int onShellCommand(Shell shell, String cmd) { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 507f398d2422..d5109123ab12 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1825,14 +1825,28 @@ public class AudioService extends IAudioService.Stub && streamTypeAlias == AudioSystem.STREAM_MUSIC // vol change on a full volume device && ((device & mFullVolumeDevices) != 0)) { - int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN : - KeyEvent.KEYCODE_VOLUME_UP; - final long ident = Binder.clearCallingIdentity(); - try { - mHdmiPlaybackClient.sendKeyEvent(keyCode, true); - mHdmiPlaybackClient.sendKeyEvent(keyCode, false); - } finally { - Binder.restoreCallingIdentity(ident); + int keyCode = KeyEvent.KEYCODE_UNKNOWN; + switch (direction) { + case AudioManager.ADJUST_RAISE: + keyCode = KeyEvent.KEYCODE_VOLUME_UP; + break; + case AudioManager.ADJUST_LOWER: + keyCode = KeyEvent.KEYCODE_VOLUME_DOWN; + break; + case AudioManager.ADJUST_TOGGLE_MUTE: + keyCode = KeyEvent.KEYCODE_VOLUME_MUTE; + break; + default: + break; + } + if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { + final long ident = Binder.clearCallingIdentity(); + try { + mHdmiPlaybackClient.sendKeyEvent(keyCode, true); + mHdmiPlaybackClient.sendKeyEvent(keyCode, false); + } finally { + Binder.restoreCallingIdentity(ident); + } } } diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java index 74b72210859c..1e0f2051933c 100644 --- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java @@ -63,11 +63,11 @@ public abstract class AuthenticationClient extends ClientMonitor { */ public abstract boolean shouldFrameworkHandleLockout(); - public AuthenticationClient(Context context, Metrics metrics, + public AuthenticationClient(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId, boolean restricted, String owner, int cookie, boolean requireConfirmation) { - super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId, + super(context, constants, daemon, halDeviceId, token, listener, targetUserId, groupId, restricted, owner, cookie); mOpId = opId; mRequireConfirmation = requireConfirmation; @@ -126,7 +126,7 @@ public abstract class AuthenticationClient extends ClientMonitor { final BiometricServiceBase.ServiceListener listener = getListener(); - mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated); + mMetricsLogger.action(mConstants.actionBiometricAuth(), authenticated); boolean result = false; try { @@ -225,7 +225,7 @@ public abstract class AuthenticationClient extends ClientMonitor { final int result = getDaemonWrapper().authenticate(mOpId, getGroupId()); if (result != 0) { Slog.w(getLogTag(), "startAuthentication failed, result=" + result); - mMetricsLogger.histogram(mMetrics.tagAuthStartError(), result); + mMetricsLogger.histogram(mConstants.tagAuthStartError(), result); onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); return result; diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index 3f856d3e8eb2..d3c62bed7b5f 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -138,7 +138,7 @@ public abstract class BiometricServiceBase extends SystemService /** * @return the metrics constants for a biometric implementation. */ - protected abstract Metrics getMetrics(); + protected abstract Constants getConstants(); /** * @param userId @@ -220,7 +220,7 @@ public abstract class BiometricServiceBase extends SystemService public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId, boolean restricted, String owner, int cookie, boolean requireConfirmation) { - super(context, getMetrics(), daemon, halDeviceId, token, listener, targetUserId, + super(context, getConstants(), daemon, halDeviceId, token, listener, targetUserId, groupId, opId, restricted, owner, cookie, requireConfirmation); } @@ -283,7 +283,7 @@ public abstract class BiometricServiceBase extends SystemService IBinder token, ServiceListener listener, int userId, int groupId, byte[] cryptoToken, boolean restricted, String owner, final int[] disabledFeatures) { - super(context, getMetrics(), daemon, halDeviceId, token, listener, + super(context, getConstants(), daemon, halDeviceId, token, listener, userId, groupId, cryptoToken, restricted, owner, getBiometricUtils(), disabledFeatures); } @@ -302,7 +302,7 @@ public abstract class BiometricServiceBase extends SystemService DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int templateId, int groupId, int userId, boolean restricted, String owner) { - super(context, getMetrics(), daemon, halDeviceId, token, listener, templateId, groupId, + super(context, getConstants(), daemon, halDeviceId, token, listener, templateId, groupId, userId, restricted, owner, getBiometricUtils()); } @@ -329,7 +329,7 @@ public abstract class BiometricServiceBase extends SystemService ServiceListener listener, int groupId, int userId, boolean restricted, String owner, List<? extends BiometricAuthenticator.Identifier> enrolledList, BiometricUtils utils) { - super(context, getMetrics(), daemon, halDeviceId, token, listener, groupId, userId, + super(context, getConstants(), daemon, halDeviceId, token, listener, groupId, userId, restricted, owner); mEnrolledList = enrolledList; mUtils = utils; @@ -655,7 +655,7 @@ public abstract class BiometricServiceBase extends SystemService @Override public void serviceDied(long cookie) { Slog.e(getTag(), "HAL died"); - mMetricsLogger.count(getMetrics().tagHalDied(), 1); + mMetricsLogger.count(getConstants().tagHalDied(), 1); mHALDeathCount++; mCurrentUserId = UserHandle.USER_NULL; handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, @@ -845,7 +845,7 @@ public abstract class BiometricServiceBase extends SystemService } mHandler.post(() -> { - mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0); + mMetricsLogger.histogram(getConstants().tagAuthToken(), opId != 0L ? 1 : 0); // Get performance stats object for this user. HashMap<Integer, PerformanceStats> pmap diff --git a/services/core/java/com/android/server/biometrics/ClientMonitor.java b/services/core/java/com/android/server/biometrics/ClientMonitor.java index 006558080ef0..942e0501d88d 100644 --- a/services/core/java/com/android/server/biometrics/ClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/ClientMonitor.java @@ -63,7 +63,7 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D private final int mCookie; protected final MetricsLogger mMetricsLogger; - protected final Metrics mMetrics; + protected final Constants mConstants; protected boolean mAlreadyCancelled; protected boolean mAlreadyDone; @@ -80,12 +80,12 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D * permission * @param owner name of the client that owns this */ - public ClientMonitor(Context context, Metrics metrics, + public ClientMonitor(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int userId, int groupId, boolean restricted, String owner, int cookie) { mContext = context; - mMetrics = metrics; + mConstants = constants; mDaemon = daemon; mHalDeviceId = halDeviceId; mToken = token; @@ -108,7 +108,7 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D } protected String getLogTag() { - return mMetrics.logTag(); + return mConstants.logTag(); } public int getCookie() { @@ -145,6 +145,31 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D public abstract boolean onEnumerationResult( BiometricAuthenticator.Identifier identifier, int remaining); + public int[] getAcquireIgnorelist() { + return new int[0]; + } + public int[] getAcquireVendorIgnorelist() { + return new int[0]; + } + + private boolean blacklistContains(int acquiredInfo, int vendorCode) { + if (acquiredInfo == mConstants.acquireVendorCode()) { + for (int i = 0; i < getAcquireVendorIgnorelist().length; i++) { + if (getAcquireVendorIgnorelist()[i] == vendorCode) { + if (DEBUG) Slog.v(getLogTag(), "Ignoring vendor message: " + vendorCode); + return true; + } + } + } else { + for (int i = 0; i < getAcquireIgnorelist().length; i++) { + if (getAcquireIgnorelist()[i] == acquiredInfo) { + if (DEBUG) Slog.v(getLogTag(), "Ignoring message: " + acquiredInfo); + return true; + } + } + } + return false; + } public boolean isAlreadyDone() { return mAlreadyDone; @@ -160,7 +185,7 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D super.logOnAcquired(mContext, acquiredInfo, vendorCode, getTargetUserId()); if (DEBUG) Slog.v(getLogTag(), "Acquired: " + acquiredInfo + " " + vendorCode); try { - if (mListener != null) { + if (mListener != null && !blacklistContains(acquiredInfo, vendorCode)) { mListener.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode); } return false; // acquisition continues... diff --git a/services/core/java/com/android/server/biometrics/Metrics.java b/services/core/java/com/android/server/biometrics/Constants.java index 02e44e94e395..874fd428ea8a 100644 --- a/services/core/java/com/android/server/biometrics/Metrics.java +++ b/services/core/java/com/android/server/biometrics/Constants.java @@ -16,7 +16,7 @@ package com.android.server.biometrics; -public interface Metrics { +public interface Constants { /** The log tag */ String logTag(); @@ -31,4 +31,6 @@ public interface Metrics { /** Integers for MetricsLogger.action() */ int actionBiometricAuth(); int actionBiometricEnroll(); + + int acquireVendorCode(); }
\ No newline at end of file diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java index e656d9809582..854528f0654d 100644 --- a/services/core/java/com/android/server/biometrics/EnrollClient.java +++ b/services/core/java/com/android/server/biometrics/EnrollClient.java @@ -40,12 +40,12 @@ public abstract class EnrollClient extends ClientMonitor { public abstract boolean shouldVibrate(); - public EnrollClient(Context context, Metrics metrics, + public EnrollClient(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int userId, int groupId, byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils, final int[] disabledFeatures) { - super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted, + super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted, owner, 0 /* cookie */); mBiometricUtils = utils; mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length); @@ -78,7 +78,7 @@ public abstract class EnrollClient extends ClientMonitor { if (shouldVibrate()) { vibrateSuccess(); } - mMetricsLogger.action(mMetrics.actionBiometricEnroll()); + mMetricsLogger.action(mConstants.actionBiometricEnroll()); try { final BiometricServiceBase.ServiceListener listener = getListener(); if (listener != null) { @@ -105,7 +105,7 @@ public abstract class EnrollClient extends ClientMonitor { disabledFeatures); if (result != 0) { Slog.w(getLogTag(), "startEnroll failed, result=" + result); - mMetricsLogger.histogram(mMetrics.tagEnrollStartError(), result); + mMetricsLogger.histogram(mConstants.tagEnrollStartError(), result); onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); return result; diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java index 44ac0373507a..f889d2b41c16 100644 --- a/services/core/java/com/android/server/biometrics/EnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java @@ -30,11 +30,11 @@ import java.util.ArrayList; * A class to keep track of the enumeration state for a given client. */ public abstract class EnumerateClient extends ClientMonitor { - public EnumerateClient(Context context, Metrics metrics, + public EnumerateClient(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int groupId, int userId, boolean restricted, String owner) { - super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted, + super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted, owner, 0 /* cookie */); } @@ -55,7 +55,7 @@ public abstract class EnumerateClient extends ClientMonitor { if (result != 0) { Slog.w(getLogTag(), "start enumerate for user " + getTargetUserId() + " failed, result=" + result); - mMetricsLogger.histogram(mMetrics.tagEnumerateStartError(), result); + mMetricsLogger.histogram(mConstants.tagEnumerateStartError(), result); onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); return result; diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java index a18f336f3ef4..bccab7b7f67c 100644 --- a/services/core/java/com/android/server/biometrics/RemovalClient.java +++ b/services/core/java/com/android/server/biometrics/RemovalClient.java @@ -33,11 +33,11 @@ public abstract class RemovalClient extends ClientMonitor { private final int mBiometricId; private final BiometricUtils mBiometricUtils; - public RemovalClient(Context context, Metrics metrics, + public RemovalClient(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int biometricId, int groupId, int userId, boolean restricted, String owner, BiometricUtils utils) { - super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted, + super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted, owner, 0 /* cookie */); mBiometricId = biometricId; mBiometricUtils = utils; @@ -60,7 +60,7 @@ public abstract class RemovalClient extends ClientMonitor { if (result != 0) { Slog.w(getLogTag(), "startRemove with id = " + mBiometricId + " failed, result=" + result); - mMetricsLogger.histogram(mMetrics.tagRemoveStartError(), result); + mMetricsLogger.histogram(mConstants.tagRemoveStartError(), result); onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); return result; diff --git a/services/core/java/com/android/server/biometrics/face/FaceMetrics.java b/services/core/java/com/android/server/biometrics/face/FaceConstants.java index 1c5cd5a84402..143eed5db523 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceMetrics.java +++ b/services/core/java/com/android/server/biometrics/face/FaceConstants.java @@ -16,10 +16,12 @@ package com.android.server.biometrics.face; +import android.hardware.face.FaceManager; + import com.android.internal.logging.nano.MetricsProto; -import com.android.server.biometrics.Metrics; +import com.android.server.biometrics.Constants; -public class FaceMetrics implements Metrics { +public class FaceConstants implements Constants { @Override public String logTag() { return FaceService.TAG; @@ -64,4 +66,9 @@ public class FaceMetrics implements Metrics { public int actionBiometricEnroll() { return MetricsProto.MetricsEvent.ACTION_FACE_ENROLL; } + + @Override + public int acquireVendorCode() { + return FaceManager.FACE_ACQUIRED_VENDOR; + } } diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index f5a96c786c88..feb58a3ad5ce 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -65,7 +65,7 @@ import com.android.server.biometrics.AuthenticationClient; import com.android.server.biometrics.BiometricServiceBase; import com.android.server.biometrics.BiometricUtils; import com.android.server.biometrics.EnumerateClient; -import com.android.server.biometrics.Metrics; +import com.android.server.biometrics.Constants; import com.android.server.biometrics.RemovalClient; import org.json.JSONArray; @@ -131,6 +131,26 @@ public class FaceService extends BiometricServiceBase { } @Override + public int[] getAcquireIgnorelist() { + if (isBiometricPrompt()) { + return mBiometricPromptIgnoreList; + } else { + // Keyguard + return mKeyguardIgnoreList; + } + } + + @Override + public int[] getAcquireVendorIgnorelist() { + if (isBiometricPrompt()) { + return mBiometricPromptIgnoreListVendor; + } else { + // Keyguard + return mKeyguardIgnoreListVendor; + } + } + + @Override public boolean onAcquired(int acquireInfo, int vendorCode) { if (acquireInfo == FaceManager.FACE_ACQUIRED_RECALIBRATE) { @@ -205,6 +225,17 @@ public class FaceService extends BiometricServiceBase { final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, 0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures) { + + @Override + public int[] getAcquireIgnorelist() { + return mEnrollIgnoreList; + } + + @Override + public int[] getAcquireVendorIgnorelist() { + return mEnrollIgnoreListVendor; + } + @Override public boolean shouldVibrate() { return false; @@ -293,7 +324,7 @@ public class FaceService extends BiometricServiceBase { } final boolean restricted = isRestricted(); - final RemovalClient client = new RemovalClient(getContext(), getMetrics(), + final RemovalClient client = new RemovalClient(getContext(), getConstants(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId, 0 /* groupId */, userId, restricted, token.toString(), getBiometricUtils()) { @Override @@ -310,7 +341,7 @@ public class FaceService extends BiometricServiceBase { checkPermission(MANAGE_BIOMETRIC); final boolean restricted = isRestricted(); - final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(), + final EnumerateClient client = new EnumerateClient(getContext(), getConstants(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId, restricted, getContext().getOpPackageName()) { @Override @@ -638,13 +669,20 @@ public class FaceService extends BiometricServiceBase { } } - private final FaceMetrics mFaceMetrics = new FaceMetrics(); + private final FaceConstants mFaceConstants = new FaceConstants(); @GuardedBy("this") private IBiometricsFace mDaemon; // One of the AuthenticationClient constants private int mCurrentUserLockoutMode; + private int[] mBiometricPromptIgnoreList; + private int[] mBiometricPromptIgnoreListVendor; + private int[] mKeyguardIgnoreList; + private int[] mKeyguardIgnoreListVendor; + private int[] mEnrollIgnoreList; + private int[] mEnrollIgnoreListVendor; + /** * Receives callbacks from the HAL. */ @@ -830,6 +868,19 @@ public class FaceService extends BiometricServiceBase { public FaceService(Context context) { super(context); + + mBiometricPromptIgnoreList = getContext().getResources() + .getIntArray(R.array.config_face_acquire_biometricprompt_ignorelist); + mBiometricPromptIgnoreListVendor = getContext().getResources() + .getIntArray(R.array.config_face_acquire_vendor_biometricprompt_ignorelist); + mKeyguardIgnoreList = getContext().getResources() + .getIntArray(R.array.config_face_acquire_keyguard_ignorelist); + mKeyguardIgnoreListVendor = getContext().getResources() + .getIntArray(R.array.config_face_acquire_vendor_keyguard_ignorelist); + mEnrollIgnoreList = getContext().getResources() + .getIntArray(R.array.config_face_acquire_enroll_ignorelist); + mEnrollIgnoreListVendor = getContext().getResources() + .getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist); } @Override @@ -855,8 +906,8 @@ public class FaceService extends BiometricServiceBase { } @Override - protected Metrics getMetrics() { - return mFaceMetrics; + protected Constants getConstants() { + return mFaceConstants; } @Override @@ -887,7 +938,7 @@ public class FaceService extends BiometricServiceBase { try { userId = getUserOrWorkProfileId(clientPackage, userId); if (userId != mCurrentUserId) { - final File baseDir = Environment.getDataVendorCeDirectory(userId); + final File baseDir = Environment.getDataVendorDeDirectory(userId); final File faceDir = new File(baseDir, FACE_DATA_DIR); if (!faceDir.exists()) { if (!faceDir.mkdir()) { diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintConstants.java index a1115c8bcfa7..bdaff71a17b9 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintConstants.java @@ -16,10 +16,12 @@ package com.android.server.biometrics.fingerprint; +import android.hardware.fingerprint.FingerprintManager; + import com.android.internal.logging.nano.MetricsProto; -import com.android.server.biometrics.Metrics; +import com.android.server.biometrics.Constants; -public class FingerprintMetrics implements Metrics { +public class FingerprintConstants implements Constants { @Override public String logTag() { @@ -65,4 +67,9 @@ public class FingerprintMetrics implements Metrics { public int actionBiometricEnroll() { return MetricsProto.MetricsEvent.ACTION_FINGERPRINT_ENROLL; } + + @Override + public int acquireVendorCode() { + return FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR; + } } diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java index 6ebeaf9ebe59..d91670d20a54 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -68,7 +68,7 @@ import com.android.server.biometrics.BiometricServiceBase; import com.android.server.biometrics.BiometricUtils; import com.android.server.biometrics.ClientMonitor; import com.android.server.biometrics.EnumerateClient; -import com.android.server.biometrics.Metrics; +import com.android.server.biometrics.Constants; import com.android.server.biometrics.RemovalClient; import org.json.JSONArray; @@ -284,7 +284,7 @@ public class FingerprintService extends BiometricServiceBase { } final boolean restricted = isRestricted(); - final RemovalClient client = new RemovalClient(getContext(), getMetrics(), + final RemovalClient client = new RemovalClient(getContext(), getConstants(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), fingerId, groupId, userId, restricted, token.toString(), getBiometricUtils()) { @Override @@ -301,7 +301,7 @@ public class FingerprintService extends BiometricServiceBase { checkPermission(MANAGE_FINGERPRINT); final boolean restricted = isRestricted(); - final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(), + final EnumerateClient client = new EnumerateClient(getContext(), getConstants(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId, restricted, getContext().getOpPackageName()) { @Override @@ -557,7 +557,7 @@ public class FingerprintService extends BiometricServiceBase { } } - private final FingerprintMetrics mFingerprintMetrics = new FingerprintMetrics(); + private final FingerprintConstants mFingerprintConstants = new FingerprintConstants(); private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks = new CopyOnWriteArrayList<>(); @@ -736,8 +736,8 @@ public class FingerprintService extends BiometricServiceBase { } @Override - protected Metrics getMetrics() { - return mFingerprintMetrics; + protected Constants getConstants() { + return mFingerprintConstants; } @Override diff --git a/services/core/java/com/android/server/biometrics/iris/IrisService.java b/services/core/java/com/android/server/biometrics/iris/IrisService.java index cb8a772356cc..2817315ac175 100644 --- a/services/core/java/com/android/server/biometrics/iris/IrisService.java +++ b/services/core/java/com/android/server/biometrics/iris/IrisService.java @@ -23,7 +23,7 @@ import android.hardware.biometrics.BiometricsProtoEnums; import com.android.server.biometrics.AuthenticationClient; import com.android.server.biometrics.BiometricServiceBase; import com.android.server.biometrics.BiometricUtils; -import com.android.server.biometrics.Metrics; +import com.android.server.biometrics.Constants; import java.util.List; @@ -75,7 +75,7 @@ public class IrisService extends BiometricServiceBase { } @Override - protected Metrics getMetrics() { + protected Constants getConstants() { return null; } diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index ba4dcdbb7ee3..998ee1ed9ebe 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -1104,11 +1104,13 @@ public final class ContentService extends IContentService.Stub { @Override public void addStatusChangeListener(int mask, ISyncStatusObserver callback) { + final int callingUid = Binder.getCallingUid(); long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); if (syncManager != null && callback != null) { - syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback); + syncManager.getSyncStorageEngine().addStatusChangeListener( + mask, UserHandle.getUserId(callingUid), callback); } } finally { restoreCallingIdentity(identityToken); diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 9f80a83038af..7e79a12d12eb 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -3375,7 +3375,8 @@ public class SyncManager { } scheduleSyncOperationH(op); - mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, + target.userId); } /** @@ -3877,7 +3878,8 @@ public class SyncManager { EventLog.writeEvent(2720, syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP)); mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime, - resultMessage, downstreamActivity, upstreamActivity); + resultMessage, downstreamActivity, upstreamActivity, + syncOperation.target.userId); } } diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java index 6b441a0d0f30..c7a3f4b173c3 100644 --- a/services/core/java/com/android/server/content/SyncStorageEngine.java +++ b/services/core/java/com/android/server/content/SyncStorageEngine.java @@ -43,6 +43,7 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.AtomicFile; import android.util.EventLog; import android.util.Log; @@ -54,6 +55,7 @@ import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.IntPair; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -588,9 +590,10 @@ public class SyncStorageEngine { return mSyncRandomOffset; } - public void addStatusChangeListener(int mask, ISyncStatusObserver callback) { + public void addStatusChangeListener(int mask, int userId, ISyncStatusObserver callback) { synchronized (mAuthorities) { - mChangeListeners.register(callback, mask); + final long cookie = IntPair.of(userId, mask); + mChangeListeners.register(callback, cookie); } } @@ -622,14 +625,16 @@ public class SyncStorageEngine { } } - void reportChange(int which) { + void reportChange(int which, int callingUserId) { ArrayList<ISyncStatusObserver> reports = null; synchronized (mAuthorities) { int i = mChangeListeners.beginBroadcast(); while (i > 0) { i--; - Integer mask = (Integer)mChangeListeners.getBroadcastCookie(i); - if ((which & mask.intValue()) == 0) { + final long cookie = (long) mChangeListeners.getBroadcastCookie(i); + final int userId = IntPair.first(cookie); + final int mask = IntPair.second(cookie); + if ((which & mask) == 0 || callingUserId != userId) { continue; } if (reports == null) { @@ -719,7 +724,7 @@ public class SyncStorageEngine { new Bundle(), syncExemptionFlag, callingUid, callingPid); } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId); queueBackup(); } @@ -787,7 +792,7 @@ public class SyncStorageEngine { requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle(), ContentResolver.SYNC_EXEMPTION_NONE, callingUid, callingPid); } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, target.userId); } public Pair<Long, Long> getBackoff(EndPoint info) { @@ -833,7 +838,7 @@ public class SyncStorageEngine { } } if (changed) { - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info.userId); } } @@ -871,7 +876,7 @@ public class SyncStorageEngine { } public void clearAllBackoffsLocked() { - boolean changed = false; + final ArraySet<Integer> changedUserIds = new ArraySet<>(); synchronized (mAuthorities) { // Clear backoff for all sync adapters. for (AccountInfo accountInfo : mAccounts.values()) { @@ -888,14 +893,14 @@ public class SyncStorageEngine { } authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE; authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE; - changed = true; + changedUserIds.add(accountInfo.accountAndUser.userId); } } } } - if (changed) { - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + for (int i = changedUserIds.size() - 1; i > 0; i--) { + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, changedUserIds.valueAt(i)); } } @@ -921,7 +926,7 @@ public class SyncStorageEngine { } authority.delayUntil = delayUntil; } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info.userId); } /** @@ -964,7 +969,7 @@ public class SyncStorageEngine { new Bundle(), syncExemptionFlag, callingUid, callingPid); } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId); mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED); queueBackup(); } @@ -1015,7 +1020,7 @@ public class SyncStorageEngine { SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); status.pending = pendingValue; } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING); + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING, info.userId); } /** @@ -1103,7 +1108,7 @@ public class SyncStorageEngine { activeSyncContext.mStartTime); getCurrentSyncs(authorityInfo.target.userId).add(syncInfo); } - reportActiveChange(); + reportActiveChange(activeSyncContext.mSyncOperation.target.userId); return syncInfo; } @@ -1120,14 +1125,14 @@ public class SyncStorageEngine { getCurrentSyncs(userId).remove(syncInfo); } - reportActiveChange(); + reportActiveChange(userId); } /** * To allow others to send active change reports, to poke clients. */ - public void reportActiveChange() { - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE); + public void reportActiveChange(int userId) { + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, userId); } /** @@ -1162,12 +1167,12 @@ public class SyncStorageEngine { if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "returning historyId " + id); } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS); + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, op.target.userId); return id; } public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage, - long downstreamActivity, long upstreamActivity) { + long downstreamActivity, long upstreamActivity, int userId) { synchronized (mAuthorities) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Slog.v(TAG, "stopSyncEvent: historyId=" + historyId); @@ -1307,7 +1312,7 @@ public class SyncStorageEngine { } } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS); + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, userId); } /** diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 3abd0ba29871..6d0137551729 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -16,12 +16,15 @@ package com.android.server.display; +import static android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT; +import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT; +import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR; -import static android.hardware.display.DisplayManager - .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL; @@ -1979,6 +1982,18 @@ public final class DisplayManagerService extends SystemService { } } + // Sometimes users can have sensitive information in system decoration windows. An app + // could create a virtual display with system decorations support and read the user info + // from the surface. + // We should only allow adding flag VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS + // to virtual displays that are owned by the system. + if (callingUid != Process.SYSTEM_UID + && (flags & VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) { + if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "createVirtualDisplay()")) { + throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); + } + } + final long token = Binder.clearCallingIdentity(); try { return createVirtualDisplayInternal(callback, projection, callingUid, packageName, @@ -2279,9 +2294,7 @@ public final class DisplayManagerService extends SystemService { Slog.e(TAG, "Unable to query projection service for permissions", e); } } - if (mContext.checkCallingPermission( - android.Manifest.permission.CAPTURE_VIDEO_OUTPUT) - == PackageManager.PERMISSION_GRANTED) { + if (checkCallingPermission(CAPTURE_VIDEO_OUTPUT, "canProjectVideo()")) { return true; } return canProjectSecureVideo(projection); @@ -2297,9 +2310,17 @@ public final class DisplayManagerService extends SystemService { Slog.e(TAG, "Unable to query projection service for permissions", e); } } - return mContext.checkCallingPermission( - android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT) - == PackageManager.PERMISSION_GRANTED; + return checkCallingPermission(CAPTURE_SECURE_VIDEO_OUTPUT, "canProjectSecureVideo()"); + } + + private boolean checkCallingPermission(String permission, String func) { + if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) { + return true; + } + final String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + " requires " + permission; + Slog.w(TAG, msg); + return false; } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 55191dbbbf40..cdcb64143ed6 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -26,6 +26,7 @@ import static android.app.Notification.FLAG_ONGOING_EVENT; import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED; import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED; import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED; +import static android.app.NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.IMPORTANCE_NONE; @@ -111,6 +112,7 @@ import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.app.PendingIntent; import android.app.Person; +import android.app.RemoteInput; import android.app.StatusBarManager; import android.app.UriGrantsManager; import android.app.admin.DeviceAdminInfo; @@ -3754,7 +3756,7 @@ public class NotificationManagerService extends SystemService { pkg, userId, true, granted); getContext().sendBroadcastAsUser(new Intent( - NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) + ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) .setPackage(pkg) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT), UserHandle.of(userId), null); @@ -3914,7 +3916,7 @@ public class NotificationManagerService extends SystemService { userId, true, granted); getContext().sendBroadcastAsUser(new Intent( - NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) + ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) .setPackage(listener.getPackageName()) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.of(userId), null); @@ -3930,7 +3932,9 @@ public class NotificationManagerService extends SystemService { public void setNotificationAssistantAccessGrantedForUser(ComponentName assistant, int userId, boolean granted) { checkCallerIsSystemOrSystemUiOrShell(); - mAssistants.setUserSet(userId, true); + for (UserInfo ui : mUm.getEnabledProfiles(userId)) { + mAssistants.setUserSet(ui.id, true); + } final long identity = Binder.clearCallingIdentity(); try { setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted); @@ -4144,30 +4148,36 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting protected void setNotificationAssistantAccessGrantedForUserInternal( - ComponentName assistant, int userId, boolean granted) { - if (assistant == null) { - ComponentName allowedAssistant = CollectionUtils.firstOrNull( - mAssistants.getAllowedComponents(userId)); - if (allowedAssistant != null) { - setNotificationAssistantAccessGrantedForUserInternal( - allowedAssistant, userId, false); - } - return; - } - if (!granted || mAllowedManagedServicePackages.test(assistant.getPackageName(), userId, - mAssistants.getRequiredPermission())) { - mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(), - userId, false, granted); - mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(), - userId, true, granted); + ComponentName assistant, int baseUserId, boolean granted) { + List<UserInfo> users = mUm.getEnabledProfiles(baseUserId); + if (users != null) { + for (UserInfo user : users) { + int userId = user.id; + if (assistant == null) { + ComponentName allowedAssistant = CollectionUtils.firstOrNull( + mAssistants.getAllowedComponents(userId)); + if (allowedAssistant != null) { + setNotificationAssistantAccessGrantedForUserInternal( + allowedAssistant, userId, false); + } + continue; + } + if (!granted || mAllowedManagedServicePackages.test(assistant.getPackageName(), + userId, mAssistants.getRequiredPermission())) { + mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(), + userId, false, granted); + mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(), + userId, true, granted); - getContext().sendBroadcastAsUser(new Intent( - NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) - .setPackage(assistant.getPackageName()) - .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), - UserHandle.of(userId), null); + getContext().sendBroadcastAsUser( + new Intent(ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) + .setPackage(assistant.getPackageName()) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), + UserHandle.of(userId), null); - handleSavePolicyFile(); + handleSavePolicyFile(); + } + } } } @@ -4834,17 +4844,35 @@ public class NotificationManagerService extends SystemService { : null; boolean isForegroundCall = CATEGORY_CALL.equals(notification.category) && (notification.flags & FLAG_FOREGROUND_SERVICE) != 0; - // OR message style (which always has a person) + // OR message style (which always has a person) with any remote input Class<? extends Notification.Style> style = notification.getNotificationStyle(); boolean isMessageStyle = Notification.MessagingStyle.class.equals(style); - boolean notificationAppropriateToBubble = isMessageStyle + boolean notificationAppropriateToBubble = + (isMessageStyle && hasValidRemoteInput(notification)) || (peopleList != null && !peopleList.isEmpty() && isForegroundCall); + // OR something that was previously a bubble & still exists boolean bubbleUpdate = oldRecord != null && (oldRecord.getNotification().flags & FLAG_BUBBLE) != 0; return canBubble && (notificationAppropriateToBubble || appIsForeground || bubbleUpdate); } + private boolean hasValidRemoteInput(Notification n) { + // Also check for inline reply + Notification.Action[] actions = n.actions; + if (actions != null) { + // Get the remote inputs + for (int i = 0; i < actions.length; i++) { + Notification.Action action = actions[i]; + RemoteInput[] inputs = action.getRemoteInputs(); + if (inputs != null && inputs.length > 0) { + return true; + } + } + } + return false; + } + private void doChannelWarningToast(CharSequence toastText) { Binder.withCleanCallingIdentity(() -> { final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0; diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java index 3a7919a0d9ba..d6108b7cc390 100644 --- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java @@ -24,6 +24,7 @@ import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.AppOpsManager; import android.app.IApplicationThread; +import android.app.admin.DevicePolicyEventLogger; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -37,6 +38,7 @@ import android.os.Binder; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.stats.devicepolicy.DevicePolicyEnums; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; @@ -69,6 +71,11 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { verifyCallingPackage(callingPackage); + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.CROSS_PROFILE_APPS_GET_TARGET_USER_PROFILES) + .setStrings(new String[] {callingPackage}) + .write(); + return getTargetUserProfilesUnchecked( callingPackage, mInjector.getCallingUserId()); } @@ -85,6 +92,11 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { verifyCallingPackage(callingPackage); + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.CROSS_PROFILE_APPS_START_ACTIVITY_AS_USER) + .setStrings(new String[] {callingPackage}) + .write(); + final int callerUserId = mInjector.getCallingUserId(); final int callingUid = mInjector.getCallingUid(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 20d47ed7c1a6..a64ae9c5fe85 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2923,28 +2923,50 @@ public class PackageManagerService extends IPackageManager.Stub // Remove disable package settings for updated system apps that were // removed via an OTA. If the update is no longer present, remove the // app completely. Otherwise, revoke their system privileges. - for (String deletedAppName : possiblyDeletedUpdatedSystemApps) { - PackageParser.Package deletedPkg = mPackages.get(deletedAppName); - mSettings.removeDisabledSystemPackageLPw(deletedAppName); + for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) { + final String packageName = possiblyDeletedUpdatedSystemApps.get(i); + final PackageParser.Package pkg = mPackages.get(packageName); final String msg; - if (deletedPkg == null) { + + // remove from the disabled system list; do this first so any future + // scans of this package are performed without this state + mSettings.removeDisabledSystemPackageLPw(packageName); + + if (pkg == null) { // should have found an update, but, we didn't; remove everything - msg = "Updated system package " + deletedAppName + msg = "Updated system package " + packageName + " no longer exists; removing its data"; // Actual deletion of code and data will be handled by later // reconciliation step } else { // found an update; revoke system privileges - msg = "Updated system package + " + deletedAppName - + " no longer exists; revoking system privileges"; + msg = "Updated system package " + packageName + + " no longer exists; rescanning package on data"; + + // NOTE: We don't do anything special if a stub is removed from the + // system image. But, if we were [like removing the uncompressed + // version from the /data partition], this is where it'd be done. + + // remove the package from the system and re-scan it without any + // special privileges + removePackageLI(pkg, true); + try { + final File codePath = new File(pkg.applicationInfo.getCodePath()); + scanPackageTracedLI(codePath, 0, scanFlags, 0, null); + } catch (PackageManagerException e) { + Slog.e(TAG, "Failed to parse updated, ex-system package: " + + e.getMessage()); + } + } - // Don't do anything if a stub is removed from the system image. If - // we were to remove the uncompressed version from the /data partition, - // this is where it'd be done. + // one final check. if we still have a package setting [ie. it was + // previously scanned and known to the system], but, we don't have + // a package [ie. there was an error scanning it from the /data + // partition], completely remove the package data. + final PackageSetting ps = mSettings.mPackages.get(packageName); + if (ps != null && mPackages.get(packageName) == null) { + removePackageDataLIF(ps, null, null, 0, false); - final PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName); - deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM; - deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM; } logCriticalInfo(Log.WARN, msg); } @@ -14974,12 +14996,14 @@ public class PackageManagerService extends IPackageManager.Stub final int installReason; @Nullable MultiPackageInstallParams mParentInstallParams; + final long requiredInstalledVersionCode; InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer, int installFlags, String installerPackageName, String volumeUuid, VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride, String[] grantedPermissions, List<String> whitelistedRestrictedPermissions, - PackageParser.SigningDetails signingDetails, int installReason) { + PackageParser.SigningDetails signingDetails, int installReason, + long requiredInstalledVersionCode) { super(user); this.origin = origin; this.move = move; @@ -14993,6 +15017,7 @@ public class PackageManagerService extends IPackageManager.Stub this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions; this.signingDetails = signingDetails; this.installReason = installReason; + this.requiredInstalledVersionCode = requiredInstalledVersionCode; } InstallParams(ActiveInstallSession activeInstallSession) { @@ -15023,6 +15048,8 @@ public class PackageManagerService extends IPackageManager.Stub whitelistedRestrictedPermissions = activeInstallSession.getSessionParams() .whitelistedRestrictedPermissions; signingDetails = activeInstallSession.getSigningDetails(); + requiredInstalledVersionCode = activeInstallSession.getSessionParams() + .requiredInstalledVersionCode; } @Override @@ -15051,6 +15078,23 @@ public class PackageManagerService extends IPackageManager.Stub } } + if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) { + if (dataOwnerPkg == null) { + Slog.w(TAG, "Required installed version code was " + + requiredInstalledVersionCode + + " but package is not installed"); + return PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION; + } + + if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) { + Slog.w(TAG, "Required installed version code was " + + requiredInstalledVersionCode + + " but actual installed version is " + + dataOwnerPkg.getLongVersionCode()); + return PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION; + } + } + if (dataOwnerPkg != null) { if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, dataOwnerPkg.applicationInfo.flags)) { @@ -15177,6 +15221,8 @@ public class PackageManagerService extends IPackageManager.Stub loc = installLocationPolicy(pkgLite); if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) { ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; + } else if (loc == PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION) { + ret = PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION; } else if (!onInt) { // Override install location with flags if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) { @@ -18762,6 +18808,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean installedStateChanged = false; if (deletedPs != null) { if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { + final SparseBooleanArray changedUsers = new SparseBooleanArray(); synchronized (mPackages) { clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL); clearDefaultBrowserIfNeeded(packageName); @@ -18793,10 +18840,9 @@ public class PackageManagerService extends IPackageManager.Stub } } } + clearPackagePreferredActivitiesLPw( + deletedPs.name, changedUsers, UserHandle.USER_ALL); } - final SparseBooleanArray changedUsers = new SparseBooleanArray(); - clearPackagePreferredActivitiesLPw( - deletedPs.name, changedUsers, UserHandle.USER_ALL); if (changedUsers.size() > 0) { updateDefaultHomeNotLocked(changedUsers); postPreferredActivityChangedBroadcast(UserHandle.USER_ALL); @@ -23289,7 +23335,7 @@ public class PackageManagerService extends IPackageManager.Stub installerPackageName, volumeUuid, null /*verificationInfo*/, user, packageAbiOverride, null /*grantedPermissions*/, null /*whitelistedRestrictedPermissions*/, PackageParser.SigningDetails.UNKNOWN, - PackageManager.INSTALL_REASON_UNKNOWN); + PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST); params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params)); msg.obj = params; @@ -23915,6 +23961,18 @@ public class PackageManagerService extends IPackageManager.Stub } @Override + public int getTargetSdkVersionForPackage(String packageName) + throws RemoteException { + int callingUser = UserHandle.getUserId(Binder.getCallingUid()); + ApplicationInfo info = getApplicationInfo(packageName, 0, callingUser); + if (info == null) { + throw new RemoteException( + "Couldn't get ApplicationInfo for package " + packageName); + } + return info.targetSdkVersion; + } + + @Override public boolean[] isAudioPlaybackCaptureAllowed(String[] packageNames) throws RemoteException { int callingUser = UserHandle.getUserId(Binder.getCallingUid()); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 803ab2d299e2..170d0850bf0f 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -44,13 +44,11 @@ import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.storage.IStorageManager; import android.util.Slog; import android.util.SparseArray; import android.util.apk.ApkSignatureVerifier; import com.android.internal.annotations.GuardedBy; -import com.android.internal.content.PackageHelper; import com.android.internal.os.BackgroundThread; import java.io.File; @@ -165,6 +163,22 @@ public class StagingManager { continue; } long activeVersion = activePackage.applicationInfo.longVersionCode; + if (session.params.requiredInstalledVersionCode + != PackageManager.VERSION_CODE_HIGHEST) { + if (activeVersion != session.params.requiredInstalledVersionCode) { + session.setStagedSessionFailed( + SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + "Installed version of APEX package " + newPackage.packageName + + " does not match required. Active version: " + activeVersion + + " required: " + session.params.requiredInstalledVersionCode); + + if (!mApexManager.abortActiveSession()) { + Slog.e(TAG, "Failed to abort apex session " + session.sessionId); + } + return false; + } + } + boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted( session.params.installFlags, activePackage.applicationInfo.flags); if (activeVersion > newPackage.versionCode && !allowsDowngrade) { @@ -255,21 +269,6 @@ public class StagingManager { } } - // Make sure we start a filesystem checkpoint on the next boot. - try { - IStorageManager storageManager = PackageHelper.getStorageManager(); - if (storageManager.supportsCheckpoint()) { - storageManager.startCheckpoint(1 /* numRetries */); - } - } catch (Exception e) { // TODO(b/130190815) make a RemoteException again - // While StorageManager lives in the same process, the native implementation - // it calls through lives in 'vold'; so, this call can fail if 'vold' isn't - // reachable. - // Since we can live without filesystem checkpointing, just warn in this case - // and continue. - Slog.w(TAG, "Could not start filesystem checkpoint:", e); - } - session.setStagedSessionReady(); if (sessionContainsApex(session) && !mApexManager.markStagedSessionReady(session.sessionId)) { diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index db2c742b8d4e..da88ec5007e4 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -50,6 +50,7 @@ import android.os.UserManager; // out of order to avoid merge conflict import android.os.SystemClock; import android.os.UserHandle; import android.provider.DeviceConfig; +import android.util.ArraySet; import android.util.IntArray; import android.util.Log; import android.util.SparseBooleanArray; @@ -69,11 +70,9 @@ import java.security.SecureRandom; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Random; import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; @@ -107,15 +106,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { @GuardedBy("mLock") private final SparseBooleanArray mAllocatedRollbackIds = new SparseBooleanArray(); - // Package rollback data for rollback-enabled installs that have not yet - // been committed. Maps from sessionId to rollback data. + // Package rollback data for rollbacks we are in the process of enabling. @GuardedBy("mLock") - private final Map<Integer, RollbackData> mPendingRollbacks = new HashMap<>(); - - // Map from child session id's for enabled rollbacks to their - // corresponding parent session ids. - @GuardedBy("mLock") - private final Map<Integer, Integer> mChildSessions = new HashMap<>(); + private final Set<NewRollback> mNewRollbacks = new ArraySet<>(); // The list of all rollbacks, including available and committed rollbacks. // This list is null until the rollback data has been loaded. @@ -136,7 +129,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // No need for guarding with lock because value is only accessed in handler thread. private long mRelativeBootTime = calculateRelativeBootTime(); - RollbackManagerServiceImpl(Context context) { mContext = context; // Note that we're calling onStart here because this object is only constructed on @@ -247,9 +239,20 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { }, filter, null, getHandler()); } + /** + * This method posts a blocking call to the handler thread, so it should not be called from + * that same thread. + * @throws {@link IllegalStateException} if called from {@link #mHandlerThread} + */ @Override public ParceledListSlice getAvailableRollbacks() { enforceManageRollbacks("getAvailableRollbacks"); + if (Thread.currentThread().equals(mHandlerThread)) { + Log.wtf(TAG, "Calling getAvailableRollbacks from mHandlerThread " + + "causes a deadlock"); + throw new IllegalStateException("Cannot call RollbackManager#getAvailableRollbacks " + + "from the handler thread!"); + } // Wait for the handler thread to get the list of available rollbacks // to get the most up-to-date results. This is intended to reduce test @@ -357,31 +360,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { return; } - // Verify the RollbackData is up to date with what's installed on - // device. - // TODO: We assume that between now and the time we commit the - // downgrade install, the currently installed package version does not - // change. This is not safe to assume, particularly in the case of a - // rollback racing with a roll-forward fix of a buggy package. - // Figure out how to ensure we don't commit the rollback if - // roll forward happens at the same time. - for (PackageRollbackInfo info : data.info.getPackages()) { - VersionedPackage installedVersion = getInstalledPackageVersion(info.getPackageName()); - if (installedVersion == null) { - // TODO: Test this case - sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE, - "Package to roll back is not installed"); - return; - } - - if (!packageVersionsEqual(info.getVersionRolledBackFrom(), installedVersion)) { - // TODO: Test this case - sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE, - "Package version to roll back not installed."); - return; - } - } - // Get a context for the caller to use to install the downgraded // version of the package. Context context = null; @@ -420,6 +398,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } params.setRequestDowngrade(true); + params.setRequiredInstalledVersionCode( + info.getVersionRolledBackFrom().getLongVersionCode()); if (data.isStaged()) { params.setStaged(); } @@ -649,8 +629,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // hasn't actually been updated. onPackageReplaced(apexPackageName); } - - mPackageHealthObserver.onBootCompleted(); + mPackageHealthObserver.onBootCompletedAsync(); }); } @@ -848,7 +827,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // TODO: It would be nice if package manager or package installer told // us the session directly, rather than have to search for it // ourselves. - PackageInstaller.SessionInfo session = null; // getAllSessions only returns sessions for the associated user. // Create a context with the right user so we can find the matching @@ -859,7 +837,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { return false; } - int parentSessionId = -1; + PackageInstaller.SessionInfo parentSession = null; + PackageInstaller.SessionInfo packageSession = null; PackageInstaller installer = context.getPackageManager().getPackageInstaller(); for (PackageInstaller.SessionInfo info : installer.getAllSessions()) { if (info.isMultiPackage()) { @@ -867,21 +846,21 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { PackageInstaller.SessionInfo child = installer.getSessionInfo(childId); if (sessionMatchesForEnableRollback(child, installFlags, newPackageCodePath)) { // TODO: Check we only have one matching session? - parentSessionId = info.getSessionId(); - session = child; + parentSession = info; + packageSession = child; break; } } } else if (sessionMatchesForEnableRollback(info, installFlags, newPackageCodePath)) { // TODO: Check we only have one matching session? - parentSessionId = info.getSessionId(); - session = info; + parentSession = info; + packageSession = info; break; } } - if (session == null) { - Log.e(TAG, "Unable to find session id for enabled rollback."); + if (parentSession == null || packageSession == null) { + Log.e(TAG, "Unable to find session for enabled rollback."); return false; } @@ -893,7 +872,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { ensureRollbackDataLoadedLocked(); for (int i = 0; i < mRollbacks.size(); ++i) { RollbackData data = mRollbacks.get(i); - if (data.apkSessionId == parentSessionId) { + if (data.apkSessionId == parentSession.getSessionId()) { rd = data; break; } @@ -906,7 +885,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { PackageParser.PackageLite newPackage = null; try { newPackage = PackageParser.parsePackageLite( - new File(session.resolvedBaseCodePath), 0); + new File(packageSession.resolvedBaseCodePath), 0); } catch (PackageParser.PackageParserException e) { Log.e(TAG, "Unable to parse new package", e); return false; @@ -924,16 +903,32 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { return false; } - return enableRollbackForSession(session, installedUsers, true); + NewRollback newRollback; + synchronized (mLock) { + // See if we already have a NewRollback that contains this package + // session. If not, create a NewRollback for the parent session + // that we will use for all the packages in the session. + newRollback = getNewRollbackForPackageSessionLocked(packageSession.getSessionId()); + if (newRollback == null) { + newRollback = createNewRollbackLocked(parentSession); + mNewRollbacks.add(newRollback); + } + } + + return enableRollbackForPackageSession(newRollback.data, packageSession, + installedUsers, /* snapshotUserData*/ true); } /** * Do code and userdata backups to enable rollback of the given session. * In case of multiPackage sessions, <code>session</code> should be one of * the child sessions, not the parent session. + * + * @return true on success, false on failure. */ - private boolean enableRollbackForSession(PackageInstaller.SessionInfo session, - @NonNull int[] installedUsers, boolean snapshotUserData) { + private boolean enableRollbackForPackageSession(RollbackData data, + PackageInstaller.SessionInfo session, @NonNull int[] installedUsers, + boolean snapshotUserData) { // TODO: Don't attempt to enable rollback for split installs. final int installFlags = session.installFlags; if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) { @@ -988,41 +983,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { VersionedPackage installedVersion = new VersionedPackage(packageName, pkgInfo.getLongVersionCode()); - PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion, + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo( + newVersion, installedVersion, new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */, isApex, IntArray.wrap(installedUsers), new SparseLongArray() /* ceSnapshotInodes */); - RollbackData data; - try { - int childSessionId = session.getSessionId(); - int parentSessionId = session.getParentSessionId(); - if (parentSessionId == PackageInstaller.SessionInfo.INVALID_ID) { - parentSessionId = childSessionId; - } - - synchronized (mLock) { - // TODO: no need to add to mChildSessions if childSessionId is - // the same as parentSessionId. - mChildSessions.put(childSessionId, parentSessionId); - data = mPendingRollbacks.get(parentSessionId); - if (data == null) { - int rollbackId = allocateRollbackIdLocked(); - if (session.isStaged()) { - data = mRollbackStore.createStagedRollback(rollbackId, parentSessionId); - } else { - data = mRollbackStore.createNonStagedRollback(rollbackId); - } - mPendingRollbacks.put(parentSessionId, data); - } - data.info.getPackages().add(info); - } - } catch (IOException e) { - Log.e(TAG, "Unable to create rollback for " + packageName, e); - return false; - } if (snapshotUserData && !isApex) { - mAppDataRollbackHelper.snapshotAppData(data.info.getRollbackId(), info); + mAppDataRollbackHelper.snapshotAppData(data.info.getRollbackId(), packageRollbackInfo); } try { @@ -1037,6 +1005,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { Log.e(TAG, "Unable to copy package for rollback for " + packageName, e); return false; } + + synchronized (mLock) { + data.info.getPackages().add(packageRollbackInfo); + } return true; } @@ -1107,8 +1079,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { return; } + NewRollback newRollback; + synchronized (mLock) { + newRollback = createNewRollbackLocked(session); + } + if (!session.isMultiPackage()) { - if (!enableRollbackForSession(session, new int[0], false)) { + if (!enableRollbackForPackageSession(newRollback.data, session, + new int[0], /* snapshotUserData */ false)) { Log.e(TAG, "Unable to enable rollback for session: " + sessionId); result.offer(false); return; @@ -1122,7 +1100,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { result.offer(false); return; } - if (!enableRollbackForSession(childSession, new int[0], false)) { + if (!enableRollbackForPackageSession(newRollback.data, childSession, + new int[0], /* snapshotUserData */ false)) { Log.e(TAG, "Unable to enable rollback for session: " + sessionId); result.offer(false); return; @@ -1130,8 +1109,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } - completeEnableRollback(sessionId, true); - result.offer(true); + result.offer(completeEnableRollback(newRollback, true) != null); }); try { @@ -1262,9 +1240,19 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { @Override public void onFinished(int sessionId, boolean success) { - RollbackData rollback = completeEnableRollback(sessionId, success); - if (rollback != null && !rollback.isStaged()) { - makeRollbackAvailable(rollback); + NewRollback newRollback; + synchronized (mLock) { + newRollback = getNewRollbackForPackageSessionLocked(sessionId); + if (newRollback != null) { + mNewRollbacks.remove(newRollback); + } + } + + if (newRollback != null) { + RollbackData rollback = completeEnableRollback(newRollback, success); + if (rollback != null && !rollback.isStaged()) { + makeRollbackAvailable(rollback); + } } } } @@ -1274,25 +1262,22 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { * This should be called after rollback has been enabled for all packages * in the rollback. It does not make the rollback available yet. * - * @return the rollback data for a successfully enable-completed rollback. + * @return the rollback data for a successfully enable-completed rollback, + * or null on error. */ - private RollbackData completeEnableRollback(int sessionId, boolean success) { - RollbackData data = null; - synchronized (mLock) { - Integer parentSessionId = mChildSessions.remove(sessionId); - if (parentSessionId != null) { - sessionId = parentSessionId; - } - - data = mPendingRollbacks.remove(sessionId); - } - - if (data == null) { + private RollbackData completeEnableRollback(NewRollback newRollback, boolean success) { + RollbackData data = newRollback.data; + if (!success) { + // The install session was aborted, clean up the pending install. + deleteRollback(data); return null; } - if (!success) { - // The install session was aborted, clean up the pending install. + // It's safe to access data.info outside a synchronized block because + // this is running on the handler thread and all changes to the + // data.info occur on the handler thread. + if (data.info.getPackages().size() != newRollback.packageSessionIds.length) { + Log.e(TAG, "Failed to enable rollback for all packages in session."); deleteRollback(data); return null; } @@ -1376,7 +1361,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } @GuardedBy("mLock") - private int allocateRollbackIdLocked() throws IOException { + private int allocateRollbackIdLocked() { int n = 0; int rollbackId; do { @@ -1387,7 +1372,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } while (n++ < 32); - throw new IOException("Failed to allocate rollback ID"); + throw new IllegalStateException("Failed to allocate rollback ID"); } private void deleteRollback(RollbackData rollbackData) { @@ -1462,4 +1447,60 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { + Manifest.permission.TEST_MANAGE_ROLLBACKS); } } + + private static class NewRollback { + public final RollbackData data; + + /** + * Session ids for all packages in the install. + * For multi-package sessions, this is the list of child session ids. + * For normal sessions, this list is a single element with the normal + * session id. + */ + public final int[] packageSessionIds; + + NewRollback(RollbackData data, int[] packageSessionIds) { + this.data = data; + this.packageSessionIds = packageSessionIds; + } + } + + NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) { + int rollbackId = allocateRollbackIdLocked(); + final RollbackData data; + int parentSessionId = parentSession.getSessionId(); + + if (parentSession.isStaged()) { + data = mRollbackStore.createStagedRollback(rollbackId, parentSessionId); + } else { + data = mRollbackStore.createNonStagedRollback(rollbackId); + } + + int[] packageSessionIds; + if (parentSession.isMultiPackage()) { + packageSessionIds = parentSession.getChildSessionIds(); + } else { + packageSessionIds = new int[]{parentSessionId}; + } + + return new NewRollback(data, packageSessionIds); + } + + /** + * Returns the NewRollback associated with the given package session. + * Returns null if no NewRollback is found for the given package + * session. + */ + NewRollback getNewRollbackForPackageSessionLocked(int packageSessionId) { + // We expect mNewRollbacks to be a very small list; linear search + // should be plenty fast. + for (NewRollback newRollbackData : mNewRollbacks) { + for (int id : newRollbackData.packageSessionIds) { + if (id == packageSessionId) { + return newRollbackData; + } + } + } + return null; + } } diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index 748a6613091e..bcef66ce2a53 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -32,6 +32,7 @@ import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; import android.os.PowerManager; +import android.os.SystemProperties; import android.text.TextUtils; import android.util.Slog; import android.util.StatsLog; @@ -49,9 +50,12 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.Collections; import java.util.List; +import java.util.concurrent.TimeUnit; /** - * {@code PackageHealthObserver} for {@code RollbackManagerService}. + * {@link PackageHealthObserver} for {@link RollbackManagerService}. + * This class monitors crashes and triggers RollbackManager rollback accordingly. + * It also monitors native crashes for some short while after boot. * * @hide */ @@ -59,12 +63,21 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve private static final String TAG = "RollbackPackageHealthObserver"; private static final String NAME = "rollback-observer"; private static final int INVALID_ROLLBACK_ID = -1; + // TODO: make the following values configurable via DeviceConfig + private static final long NATIVE_CRASH_POLLING_INTERVAL_MILLIS = + TimeUnit.SECONDS.toMillis(30); + private static final long NUMBER_OF_NATIVE_CRASH_POLLS = 10; + private final Context mContext; private final Handler mHandler; private final File mLastStagedRollbackIdFile; + // this field is initialized in the c'tor and then only accessed from mHandler thread, so + // no need to guard with a lock + private long mNumberOfNativeCrashPollsRemaining; RollbackPackageHealthObserver(Context context) { mContext = context; + mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS; HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver"); handlerThread.start(); mHandler = handlerThread.getThreadHandler(); @@ -76,8 +89,6 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve @Override public int onHealthCheckFailed(VersionedPackage failedPackage) { - VersionedPackage moduleMetadataPackage = getModuleMetadataPackage(); - if (getAvailableRollback(mContext.getSystemService(RollbackManager.class), failedPackage) == null) { // Don't handle the notification, no rollbacks available for the package @@ -145,16 +156,29 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs); } - /** Verifies the rollback state after a reboot. */ - public void onBootCompleted() { + /** Verifies the rollback state after a reboot and schedules polling for sometime after reboot + * to check for native crashes and mitigate them if needed. + */ + public void onBootCompletedAsync() { + mHandler.post(()->onBootCompleted()); + } + + private void onBootCompleted() { + RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); + PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller(); + String moduleMetadataPackageName = getModuleMetadataPackageName(); + VersionedPackage newModuleMetadataPackage = getModuleMetadataPackage(); + + if (getAvailableRollback(rollbackManager, newModuleMetadataPackage) != null) { + scheduleCheckAndMitigateNativeCrashes(); + } + int rollbackId = popLastStagedRollbackId(); if (rollbackId == INVALID_ROLLBACK_ID) { // No staged rollback before reboot return; } - RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); - PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller(); RollbackInfo rollback = null; for (RollbackInfo info : rollbackManager.getRecentlyCommittedRollbacks()) { if (rollbackId == info.getRollbackId()) { @@ -168,14 +192,12 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve return; } - String moduleMetadataPackageName = getModuleMetadataPackageName(); - // Use the version of the metadata package that was installed before // we rolled back for logging purposes. - VersionedPackage moduleMetadataPackage = null; + VersionedPackage oldModuleMetadataPackage = null; for (PackageRollbackInfo packageRollback : rollback.getPackages()) { if (packageRollback.getPackageName().equals(moduleMetadataPackageName)) { - moduleMetadataPackage = packageRollback.getVersionRolledBackFrom(); + oldModuleMetadataPackage = packageRollback.getVersionRolledBackFrom(); break; } } @@ -187,12 +209,12 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve return; } if (sessionInfo.isStagedSessionApplied()) { - logEvent(moduleMetadataPackage, + logEvent(oldModuleMetadataPackage, StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS); } else if (sessionInfo.isStagedSessionReady()) { // TODO: What do for staged session ready but not applied } else { - logEvent(moduleMetadataPackage, + logEvent(oldModuleMetadataPackage, StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE); } } @@ -320,4 +342,34 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve moduleMetadataPackage.getPackageName(), moduleMetadataPackage.getVersionCode()); } } + + /** + * This method should be only called on mHandler thread, since it modifies + * {@link #mNumberOfNativeCrashPollsRemaining} and we want to keep this class lock free. + */ + private void checkAndMitigateNativeCrashes() { + mNumberOfNativeCrashPollsRemaining--; + // Check if native watchdog reported a crash + if ("1".equals(SystemProperties.get("ro.init.updatable_crashing"))) { + execute(getModuleMetadataPackage()); + // we stop polling after an attempt to execute rollback, regardless of whether the + // attempt succeeds or not + } else { + if (mNumberOfNativeCrashPollsRemaining > 0) { + mHandler.postDelayed(() -> checkAndMitigateNativeCrashes(), + NATIVE_CRASH_POLLING_INTERVAL_MILLIS); + } + } + } + + /** + * Since this method can eventually trigger a RollbackManager rollback, it should be called + * only once boot has completed {@code onBootCompleted} and not earlier, because the install + * session must be entirely completed before we try to rollback. + */ + private void scheduleCheckAndMitigateNativeCrashes() { + Slog.i(TAG, "Scheduling " + mNumberOfNativeCrashPollsRemaining + " polls to check " + + "and mitigate native crashes"); + mHandler.post(()->checkAndMitigateNativeCrashes()); + } } diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index 2cfa46532834..8a26368c3e13 100644 --- a/services/core/java/com/android/server/rollback/RollbackStore.java +++ b/services/core/java/com/android/server/rollback/RollbackStore.java @@ -194,7 +194,7 @@ class RollbackStore { * Creates a new RollbackData instance for a non-staged rollback with * backupDir assigned. */ - RollbackData createNonStagedRollback(int rollbackId) throws IOException { + RollbackData createNonStagedRollback(int rollbackId) { File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId)); return new RollbackData(rollbackId, backupDir, -1); } @@ -203,8 +203,7 @@ class RollbackStore { * Creates a new RollbackData instance for a staged rollback with * backupDir assigned. */ - RollbackData createStagedRollback(int rollbackId, int stagedSessionId) - throws IOException { + RollbackData createStagedRollback(int rollbackId, int stagedSessionId) { File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId)); return new RollbackData(rollbackId, backupDir, stagedSessionId); } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index b9b5aae03587..7734d6bd30a7 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -77,7 +77,8 @@ public interface StatusBarManagerInternal { void onCameraLaunchGestureDetected(int source); void topAppWindowChanged(int displayId, boolean menuVisible); void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis, - int mask, Rect fullscreenBounds, Rect dockedBounds, String cause); + int mask, Rect fullscreenBounds, Rect dockedBounds, boolean isNavbarColorManagedByIme, + String cause); void toggleSplitScreen(); void appTransitionFinished(int displayId); diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index a5656c3e40f0..b2d7084fcd05 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -262,9 +262,10 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D @Override public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds, - String cause) { + boolean isNavbarColorManagedByIme, String cause) { StatusBarManagerService.this.setSystemUiVisibility(displayId, vis, fullscreenStackVis, - dockedStackVis, mask, fullscreenBounds, dockedBounds, cause); + dockedStackVis, mask, fullscreenBounds, dockedBounds, isNavbarColorManagedByIme, + cause); } @Override @@ -872,11 +873,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D public void setSystemUiVisibility(int displayId, int vis, int mask, String cause) { final UiState state = getUiState(displayId); setSystemUiVisibility(displayId, vis, 0, 0, mask, - state.mFullscreenStackBounds, state.mDockedStackBounds, cause); + state.mFullscreenStackBounds, state.mDockedStackBounds, + state.mNavbarColorManagedByIme, cause); } private void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, - int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds, String cause) { + int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds, + boolean isNavbarColorManagedByIme, String cause) { // also allows calls from window manager which is in this process. enforceStatusBarService(); @@ -884,7 +887,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D synchronized (mLock) { updateUiVisibilityLocked(displayId, vis, fullscreenStackVis, dockedStackVis, mask, - fullscreenBounds, dockedBounds); + fullscreenBounds, dockedBounds, isNavbarColorManagedByIme); disableLocked( displayId, mCurrentUserId, @@ -896,17 +899,19 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D private void updateUiVisibilityLocked(final int displayId, final int vis, final int fullscreenStackVis, final int dockedStackVis, final int mask, - final Rect fullscreenBounds, final Rect dockedBounds) { + final Rect fullscreenBounds, final Rect dockedBounds, + final boolean isNavbarColorManagedByIme) { final UiState state = getUiState(displayId); if (!state.systemUiStateEquals(vis, fullscreenStackVis, dockedStackVis, - fullscreenBounds, dockedBounds)) { + fullscreenBounds, dockedBounds, isNavbarColorManagedByIme)) { state.setSystemUiState(vis, fullscreenStackVis, dockedStackVis, fullscreenBounds, - dockedBounds); + dockedBounds, isNavbarColorManagedByIme); mHandler.post(() -> { if (mBar != null) { try { mBar.setSystemUiVisibility(displayId, vis, fullscreenStackVis, - dockedStackVis, mask, fullscreenBounds, dockedBounds); + dockedStackVis, mask, fullscreenBounds, dockedBounds, + isNavbarColorManagedByIme); } catch (RemoteException ex) { Log.w(TAG, "Can not get StatusBar!"); } @@ -945,6 +950,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D private int mImeBackDisposition = 0; private boolean mShowImeSwitcher = false; private IBinder mImeToken = null; + private boolean mNavbarColorManagedByIme = false; private int getDisabled1() { return mDisabled1; @@ -972,21 +978,25 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } private void setSystemUiState(final int vis, final int fullscreenStackVis, - final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) { + final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds, + final boolean navbarColorManagedByIme) { mSystemUiVisibility = vis; mFullscreenStackSysUiVisibility = fullscreenStackVis; mDockedStackSysUiVisibility = dockedStackVis; mFullscreenStackBounds.set(fullscreenBounds); mDockedStackBounds.set(dockedBounds); + mNavbarColorManagedByIme = navbarColorManagedByIme; } private boolean systemUiStateEquals(final int vis, final int fullscreenStackVis, - final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) { + final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds, + final boolean navbarColorManagedByIme) { return mSystemUiVisibility == vis && mFullscreenStackSysUiVisibility == fullscreenStackVis && mDockedStackSysUiVisibility == dockedStackVis && mFullscreenStackBounds.equals(fullscreenBounds) - && mDockedStackBounds.equals(dockedBounds); + && mDockedStackBounds.equals(dockedBounds) + && mNavbarColorManagedByIme == navbarColorManagedByIme; } private void setImeWindowState(final int vis, final int backDisposition, @@ -1051,7 +1061,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D state.mImeBackDisposition, state.mShowImeSwitcher, gatherDisableActionsLocked(mCurrentUserId, 2), state.mFullscreenStackSysUiVisibility, state.mDockedStackSysUiVisibility, - state.mImeToken, state.mFullscreenStackBounds, state.mDockedStackBounds); + state.mImeToken, state.mFullscreenStackBounds, state.mDockedStackBounds, + state.mNavbarColorManagedByIme); } } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 6ce42ec4cb44..b6a5be807fb6 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -1108,6 +1108,7 @@ final class AccessibilityController { // the window manager is still looking for where to put it. // We will do the work when we get a focus change callback. // TODO(b/112273690): Support multiple displays + // TODO(b/129098348): Support embedded displays if (mService.getDefaultDisplayContentLocked().mCurrentFocus == null) { return; } @@ -1400,7 +1401,28 @@ final class AccessibilityController { if (w.isVisibleLw()) { outWindows.put(mTempLayer++, w); } - }, false /* traverseTopToBottom */ ); + }, false /* traverseTopToBottom */); + mService.mRoot.forAllWindows(w -> { + final WindowState win = findRootDisplayParentWindow(w); + if (win != null && win.getDisplayContent().isDefaultDisplay && w.isVisibleLw()) { + // TODO(b/129098348): insert windows on child displays into outWindows based on + // root-display-parent window. + outWindows.put(mTempLayer++, w); + } + }, false /* traverseTopToBottom */); + } + + private WindowState findRootDisplayParentWindow(WindowState win) { + WindowState displayParentWindow = win.getDisplayContent().getParentWindow(); + if (displayParentWindow == null) { + return null; + } + WindowState candidate = displayParentWindow; + while (candidate != null) { + displayParentWindow = candidate; + candidate = displayParentWindow.getDisplayContent().getParentWindow(); + } + return displayParentWindow; } private class MyHandler extends Handler { diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 0891ba4be66f..9decb58aff2e 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -965,16 +965,13 @@ class ActivityMetricsLogger { * @param info * */ private void startTraces(WindowingModeTransitionInfo info) { - if (info == null) { + if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER) || info == null + || info.launchTraceActive) { return; } - int transitionType = getTransitionType(info); - if (!info.launchTraceActive && transitionType == TYPE_TRANSITION_WARM_LAUNCH - || transitionType == TYPE_TRANSITION_COLD_LAUNCH) { - Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: " - + info.launchedActivity.packageName, 0); - info.launchTraceActive = true; - } + Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: " + + info.launchedActivity.packageName, 0); + info.launchTraceActive = true; } private void stopLaunchTrace(WindowingModeTransitionInfo info) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 4278860a0d77..802683a67f80 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -916,8 +916,12 @@ final class ActivityRecord extends ConfigurationContainer { } } + static boolean isResolverActivity(String className) { + return ResolverActivity.class.getName().equals(className); + } + boolean isResolverActivity() { - return ResolverActivity.class.getName().equals(mActivityComponent.getClassName()); + return isResolverActivity(mActivityComponent.getClassName()); } boolean isResolverOrChildActivity() { diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 8a834c8f942b..fe99fd20b855 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -1822,7 +1822,8 @@ class ActivityStack extends ConfigurationContainer { prev.setDeferHidingClient(false); // If we were visible then resumeTopActivities will release resources before // stopping. - addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */); + addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */, + "completePauseLocked"); } } else { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "App died during pause, not stopping: " + prev); @@ -1883,8 +1884,11 @@ class ActivityStack extends ConfigurationContainer { mRootActivityContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS); } - private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) { + private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed, + String reason) { if (!mStackSupervisor.mStoppingActivities.contains(r)) { + EventLog.writeEvent(EventLogTags.AM_ADD_TO_STOPPING, r.mUserId, + System.identityHashCode(r), r.shortComponentName, reason); mStackSupervisor.mStoppingActivities.add(r); } @@ -2433,7 +2437,7 @@ class ActivityStack extends ConfigurationContainer { case PAUSING: case PAUSED: addToStopping(r, true /* scheduleIdle */, - canEnterPictureInPicture /* idleDelayed */); + canEnterPictureInPicture /* idleDelayed */, "makeInvisible"); break; default: @@ -3051,21 +3055,7 @@ class ActivityStack extends ConfigurationContainer { ActivityOptions.abort(options); if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityInNextFocusableStack: " + reason + ", go home"); - if (isActivityTypeHome()) { - // resumeTopActivityUncheckedLocked has been prevented to run recursively. Post a - // runnable to resume home since we are currently in the process of resuming top - // activity in home stack. - // See {@link #mInResumeTopActivity}. - mService.mH.post( - () -> { - synchronized (mService.mGlobalLock) { - mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId); - } - }); - return true; - } else { - return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId); - } + return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId); } /** Returns the position the input task should be placed in this stack. */ @@ -4098,7 +4088,8 @@ class ActivityStack extends ConfigurationContainer { if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible) && next != null && !next.nowVisible && !isFloating) { if (!mStackSupervisor.mStoppingActivities.contains(r)) { - addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */); + addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */, + "finishCurrentActivityLocked"); } if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPING: "+ r + " (finish requested)"); diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index 7eac07c47741..919141c13622 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -171,7 +171,12 @@ public class ActivityStartController { void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) { final ActivityOptions options = ActivityOptions.makeBasic(); options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); - options.setLaunchActivityType(ACTIVITY_TYPE_HOME); + if (!ActivityRecord.isResolverActivity(aInfo.name)) { + // The resolver activity shouldn't be put in home stack because when the foreground is + // standard type activity, the resolver activity should be put on the top of current + // foreground instead of bring home stack to front. + options.setLaunchActivityType(ACTIVITY_TYPE_HOME); + } options.setLaunchDisplayId(displayId); mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason) .setOutActivity(tmpOutRecord) diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 4ef8753bd131..e7e34bb881e4 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -160,6 +160,10 @@ class ActivityStarter { private int mCallingUid; private ActivityOptions mOptions; + // If it is true, background activity can only be started in an existing task that contains + // an activity with same uid. + private boolean mRestrictedBgActivity; + private int mLaunchMode; private boolean mLaunchTaskBehind; private int mLaunchFlags; @@ -455,6 +459,7 @@ class ActivityStarter { mIntent = starter.mIntent; mCallingUid = starter.mCallingUid; mOptions = starter.mOptions; + mRestrictedBgActivity = starter.mRestrictedBgActivity; mLaunchTaskBehind = starter.mLaunchTaskBehind; mLaunchFlags = starter.mLaunchFlags; @@ -551,7 +556,8 @@ class ActivityStarter { mLastStartActivityTimeMs = System.currentTimeMillis(); mLastStartActivityRecord[0] = r; mLastStartActivityResult = startActivity(r, sourceRecord, voiceSession, voiceInteractor, - startFlags, doResume, options, inTask, mLastStartActivityRecord); + startFlags, doResume, options, inTask, mLastStartActivityRecord, + false /* restrictedBgActivity */); mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(mLastStartActivityResult, mLastStartActivityRecord[0]); return mLastStartActivityResult; @@ -760,22 +766,17 @@ class ActivityStarter { abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid, callingPid, resolvedType, aInfo.applicationInfo); - boolean abortBackgroundStart = false; + boolean restrictedBgActivity = false; if (!abort) { try { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "shouldAbortBackgroundActivityStart"); - abortBackgroundStart = shouldAbortBackgroundActivityStart(callingUid, + restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, realCallingUid, realCallingPid, callerApp, originatingPendingIntent, allowBackgroundActivityStart, intent); } finally { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } - abort |= (abortBackgroundStart && !mService.isBackgroundActivityStartsEnabled()); - // TODO: remove this toast after feature development is done - if (abortBackgroundStart) { - showBackgroundActivityBlockedToast(abort, callingPackage); - } } // Merge the two options bundles, while realCallerOptions takes precedence. @@ -918,8 +919,10 @@ class ActivityStarter { || stack.getResumedActivity().info.applicationInfo.uid != realCallingUid)) { if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, realCallingPid, realCallingUid, "Activity start")) { - mController.addPendingActivityLaunch(new PendingActivityLaunch(r, - sourceRecord, startFlags, stack, callerApp)); + if (!restrictedBgActivity) { + mController.addPendingActivityLaunch(new PendingActivityLaunch(r, + sourceRecord, startFlags, stack, callerApp)); + } ActivityOptions.abort(checkedOptions); return ActivityManager.START_SWITCHES_CANCELED; } @@ -929,7 +932,7 @@ class ActivityStarter { mController.doPendingActivityLaunches(false); final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, - true /* doResume */, checkedOptions, inTask, outActivity); + true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity); mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outActivity[0]); return res; } @@ -1038,6 +1041,12 @@ class ActivityStarter { } } } + // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission + if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) { + Slog.w(TAG, "Background activity start for " + callingPackage + + " allowed because SYSTEM_ALERT_WINDOW permission is granted."); + return false; + } // anything that has fallen through would currently be aborted Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage + "; callingUid: " + callingUid @@ -1395,13 +1404,13 @@ class ActivityStarter { private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, - ActivityRecord[] outActivity) { + ActivityRecord[] outActivity, boolean restrictedBgActivity) { int result = START_CANCELED; final ActivityStack startedActivityStack; try { mService.mWindowManager.deferSurfaceLayout(); result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, - startFlags, doResume, options, inTask, outActivity); + startFlags, doResume, options, inTask, outActivity, restrictedBgActivity); } finally { final ActivityStack currentStack = r.getActivityStack(); startedActivityStack = currentStack != null ? currentStack : mTargetStack; @@ -1437,14 +1446,40 @@ class ActivityStarter { return result; } + /** + * Return true if background activity is really aborted. + * + * TODO(b/131748165): Refactor the logic so we don't need to call this method everywhere. + */ + private boolean handleBackgroundActivityAbort(ActivityRecord r) { + // TODO(b/131747138): Remove toast and refactor related code in Q release. + boolean abort = !mService.isBackgroundActivityStartsEnabled(); + showBackgroundActivityBlockedToast(abort, r.launchedFromPackage); + if (!abort) { + return false; + } + ActivityRecord resultRecord = r.resultTo; + String resultWho = r.resultWho; + int requestCode = r.requestCode; + if (resultRecord != null) { + ActivityStack resultStack = resultRecord.getActivityStack(); + resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode, + RESULT_CANCELED, null); + } + // We pretend to the caller that it was really started to make it backward compatible, but + // they will just get a cancel result. + ActivityOptions.abort(r.pendingOptions); + return true; + } + // Note: This method should only be called from {@link startActivity}. private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, - ActivityRecord[] outActivity) { - + ActivityRecord[] outActivity, boolean restrictedBgActivity) { setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession, - voiceInteractor); + voiceInteractor, restrictedBgActivity); + final int preferredWindowingMode = mLaunchParams.mWindowingMode; computeLaunchingTaskFlags(); @@ -1652,7 +1687,7 @@ class ActivityStarter { } else { // This not being started from an existing activity, and not part of a new task... // just put it in the top task, though these days this case should never happen. - setTaskToCurrentTopOrCreateNewTask(); + result = setTaskToCurrentTopOrCreateNewTask(); } if (result != START_SUCCESS) { return result; @@ -1725,6 +1760,7 @@ class ActivityStarter { mIntent = null; mCallingUid = -1; mOptions = null; + mRestrictedBgActivity = false; mLaunchTaskBehind = false; mLaunchFlags = 0; @@ -1764,7 +1800,8 @@ class ActivityStarter { private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask, boolean doResume, int startFlags, ActivityRecord sourceRecord, - IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) { + IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, + boolean restrictedBgActivity) { reset(false /* clearRequest */); mStartActivity = r; @@ -1774,6 +1811,7 @@ class ActivityStarter { mSourceRecord = sourceRecord; mVoiceSession = voiceSession; mVoiceInteractor = voiceInteractor; + mRestrictedBgActivity = restrictedBgActivity; mLaunchParams.reset(); @@ -1874,6 +1912,11 @@ class ActivityStarter { } mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0; + + if (restrictedBgActivity) { + mAvoidMoveToFront = true; + mDoResume = false; + } } private void sendNewTaskResultRequestIfNeeded() { @@ -2075,8 +2118,8 @@ class ActivityStarter { final ActivityRecord curTop = (focusStack == null) ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop); final TaskRecord topTask = curTop != null ? curTop.getTaskRecord() : null; - differentTopTask = topTask != null - && (topTask != intentActivity.getTaskRecord() || topTask != focusStack.topTask()); + differentTopTask = topTask != intentActivity.getTaskRecord() + || (focusStack != null && topTask != focusStack.topTask()); } else { // The existing task should always be different from those in other displays. differentTopTask = true; @@ -2271,6 +2314,9 @@ class ActivityStarter { // isLockTaskModeViolation fails below. if (mReuseTask == null) { + if (mRestrictedBgActivity && handleBackgroundActivityAbort(mStartActivity)) { + return START_ABORTED; + } final TaskRecord task = mTargetStack.createTaskRecord( mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId), mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info, @@ -2283,6 +2329,11 @@ class ActivityStarter { if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity + " in new task " + mStartActivity.getTaskRecord()); } else { + if (mRestrictedBgActivity && !mReuseTask.containsAppUid(mCallingUid)) { + if (handleBackgroundActivityAbort(mStartActivity)) { + return START_ABORTED; + } + } addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask"); } @@ -2322,6 +2373,12 @@ class ActivityStarter { final TaskRecord sourceTask = mSourceRecord.getTaskRecord(); final ActivityStack sourceStack = mSourceRecord.getActivityStack(); + if (mRestrictedBgActivity && !sourceTask.containsAppUid(mCallingUid)) { + if (handleBackgroundActivityAbort(mStartActivity)) { + return START_ABORTED; + } + return START_ABORTED; + } // We only want to allow changing stack in two cases: // 1. If the target task is not the top one. Otherwise we would move the launching task to // the other side, rather than show two side by side. @@ -2483,20 +2540,33 @@ class ActivityStarter { } } - private void setTaskToCurrentTopOrCreateNewTask() { + private int setTaskToCurrentTopOrCreateNewTask() { mTargetStack = computeStackFocus(mStartActivity, false, mLaunchFlags, mOptions); if (mDoResume) { mTargetStack.moveToFront("addingToTopTask"); } final ActivityRecord prev = mTargetStack.getTopActivity(); + if (mRestrictedBgActivity && prev == null) { + if (handleBackgroundActivityAbort(mStartActivity)) { + return START_ABORTED; + } + return START_ABORTED; + } final TaskRecord task = (prev != null) ? prev.getTaskRecord() : mTargetStack.createTaskRecord( mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId), mStartActivity.info, mIntent, null, null, true, mStartActivity, mSourceRecord, mOptions); + if (mRestrictedBgActivity && !task.containsAppUid(mCallingUid)) { + if (handleBackgroundActivityAbort(mStartActivity)) { + return START_ABORTED; + } + return START_ABORTED; + } addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask"); mTargetStack.positionChildWindowContainerAtTop(task); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity + " in new guessed " + mStartActivity.getTaskRecord()); + return START_SUCCESS; } private void addOrReparentStartingActivity(TaskRecord parent, String reason) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 7bc9600f50b4..4a6aa336e36f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -108,12 +108,10 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITC import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_ACTIVITY_ID; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE; -import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_TASK_ID; import static com.android.server.wm.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG; import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; @@ -136,6 +134,7 @@ import android.app.ActivityTaskManager; import android.app.ActivityThread; import android.app.AlertDialog; import android.app.AppGlobals; +import android.app.AppOpsManager; import android.app.Dialog; import android.app.IActivityController; import android.app.IActivityTaskManager; @@ -216,7 +215,6 @@ import android.text.format.Time; import android.util.ArrayMap; import android.util.EventLog; import android.util.Log; -import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; @@ -875,6 +873,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return getUserManager().hasUserRestriction(restriction, userId); } + boolean hasSystemAlertWindowPermission(int callingUid, int callingPid, String callingPackage) { + final int mode = getAppOpsService().noteOperation(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, + callingUid, callingPackage); + if (mode == AppOpsManager.MODE_DEFAULT) { + return checkPermission(Manifest.permission.SYSTEM_ALERT_WINDOW, callingPid, callingUid) + == PERMISSION_GRANTED; + } + return mode == AppOpsManager.MODE_ALLOWED; + } + protected RecentTasks createRecentTasks() { return new RecentTasks(this, mStackSupervisor); } diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 75e34fb0d453..d4c4e6a93fa6 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -478,7 +478,7 @@ public class AppTransitionController { outReasons.put(windowingMode, APP_TRANSITION_WINDOWS_DRAWN); } else { outReasons.put(windowingMode, - wtoken.startingData instanceof SplashScreenStartingData + wtoken.mStartingData instanceof SplashScreenStartingData ? APP_TRANSITION_SPLASH_SCREEN : APP_TRANSITION_SNAPSHOT); } diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java index 6318486683ae..4d972dca11ed 100644 --- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java +++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java @@ -129,7 +129,7 @@ class AppWindowThumbnail implements Animatable { mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter( new WindowAnimationSpec(anim, position, mAppToken.getDisplayContent().mAppTransition.canSkipFirstFrame(), - mAppToken.getWindowCornerRadiusForAnimation()), + mAppToken.getDisplayContent().getWindowCornerRadius()), mAppToken.mWmService.mSurfaceAnimationRunner), false /* hidden */); } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index a3cef7f7219d..5be8e1408578 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -78,6 +78,7 @@ import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; import static com.android.server.wm.WindowManagerService.logWithStack; +import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM; @@ -203,7 +204,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree boolean removed; // Information about an application starting window if displayed. - StartingData startingData; + StartingData mStartingData; WindowState startingWindow; StartingSurface startingSurface; boolean startingDisplayed; @@ -385,8 +386,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // it from behind the starting window, so there is no need for it to also be doing its // own stuff. win.cancelAnimation(); - removeStartingWindow(); } + removeStartingWindow(); updateReportedVisibilityLocked(); } @@ -638,8 +639,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // If we are being set visible, and the starting window is not yet displayed, // then make sure it doesn't get displayed. if (startingWindow != null && !startingWindow.isDrawnLw()) { - startingWindow.mPolicyVisibility = false; - startingWindow.mPolicyVisibilityAfterAnim = false; + startingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY); + startingWindow.mLegacyPolicyVisibilityAfterAnim = false; } // We are becoming visible, so better freeze the screen with the windows that are @@ -874,7 +875,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: " + this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4)); - if (startingData != null) { + if (mStartingData != null) { removeStartingWindow(); } @@ -1053,7 +1054,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // If this is the last window and we had requested a starting transition window, // well there is no point now. if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingData"); - startingData = null; + mStartingData = null; if (mHiddenSetFromTransferredStartingWindow) { // We set the hidden state to false for the token from a transferred starting window. // We now reset it back to true since the starting window was the last window in the @@ -1486,13 +1487,13 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree final long origId = Binder.clearCallingIdentity(); try { // Transfer the starting window over to the new token. - startingData = fromToken.startingData; + mStartingData = fromToken.mStartingData; startingSurface = fromToken.startingSurface; startingDisplayed = fromToken.startingDisplayed; fromToken.startingDisplayed = false; startingWindow = tStartingWindow; reportedVisible = fromToken.reportedVisible; - fromToken.startingData = null; + fromToken.mStartingData = null; fromToken.startingSurface = null; fromToken.startingWindow = null; fromToken.startingMoved = true; @@ -1538,13 +1539,13 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree Binder.restoreCallingIdentity(origId); } return true; - } else if (fromToken.startingData != null) { + } else if (fromToken.mStartingData != null) { // The previous app was getting ready to show a // starting window, but hasn't yet done so. Steal it! if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Moving pending starting from " + fromToken + " to " + this); - startingData = fromToken.startingData; - fromToken.startingData = null; + mStartingData = fromToken.mStartingData; + fromToken.mStartingData = null; fromToken.startingMoved = true; scheduleAddStartingWindow(); return true; @@ -1932,7 +1933,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree + ", isAnimationSet=" + isSelfAnimating()); if (!w.isDrawnLw()) { Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController - + " pv=" + w.mPolicyVisibility + + " pv=" + w.isVisibleByPolicy() + " mDrawState=" + winAnimator.drawStateToString() + " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested + " a=" + isSelfAnimating()); @@ -2042,7 +2043,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return false; } - if (startingData != null) { + if (mStartingData != null) { return false; } @@ -2123,7 +2124,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData"); - startingData = new SplashScreenStartingData(mWmService, pkg, + mStartingData = new SplashScreenStartingData(mWmService, pkg, theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, getMergedOverrideConfiguration()); scheduleAddStartingWindow(); @@ -2137,7 +2138,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData"); - startingData = new SnapshotStartingData(mWmService, snapshot); + mStartingData = new SnapshotStartingData(mWmService, snapshot); scheduleAddStartingWindow(); return true; } @@ -2156,18 +2157,21 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree @Override public void run() { + // Can be accessed without holding the global lock + final StartingData startingData; synchronized (mWmService.mGlobalLock) { // There can only be one adding request, silly caller! mWmService.mAnimationHandler.removeCallbacks(this); - } - if (startingData == null) { - // Animation has been canceled... do nothing. - if (DEBUG_STARTING_WINDOW) { - Slog.v(TAG, "startingData was nulled out before handling" - + " mAddStartingWindow: " + AppWindowToken.this); + if (mStartingData == null) { + // Animation has been canceled... do nothing. + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "startingData was nulled out before handling" + + " mAddStartingWindow: " + AppWindowToken.this); + } + return; } - return; + startingData = mStartingData; } if (DEBUG_STARTING_WINDOW) { @@ -2185,20 +2189,21 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree synchronized (mWmService.mGlobalLock) { // If the window was successfully added, then // we need to remove it. - if (removed || startingData == null) { + if (removed || mStartingData == null) { if (DEBUG_STARTING_WINDOW) { Slog.v(TAG, "Aborted starting " + AppWindowToken.this - + ": removed=" + removed + " startingData=" + startingData); + + ": removed=" + removed + " startingData=" + mStartingData); } startingWindow = null; - startingData = null; + mStartingData = null; abort = true; } else { startingSurface = surface; } if (DEBUG_STARTING_WINDOW && !abort) { - Slog.v(TAG, "Added starting " + AppWindowToken.this + ": startingWindow=" - + startingWindow + " startingView=" + startingSurface); + Slog.v(TAG, + "Added starting " + AppWindowToken.this + ": startingWindow=" + + startingWindow + " startingView=" + startingSurface); } } if (abort) { @@ -2245,21 +2250,21 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree void removeStartingWindow() { if (startingWindow == null) { - if (startingData != null) { + if (mStartingData != null) { // Starting window has not been added yet, but it is scheduled to be added. // Go ahead and cancel the request. if (DEBUG_STARTING_WINDOW) { Slog.v(TAG_WM, "Clearing startingData for token=" + this); } - startingData = null; + mStartingData = null; } return; } final WindowManagerPolicy.StartingSurface surface; - if (startingData != null) { + if (mStartingData != null) { surface = startingSurface; - startingData = null; + mStartingData = null; startingSurface = null; startingWindow = null; startingDisplayed = false; @@ -2552,12 +2557,17 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction); if (a != null) { + // Only apply corner radius to animation if we're not in multi window mode. + // We don't want rounded corners when in pip or split screen. + final float windowCornerRadius = !inMultiWindowMode() + ? getDisplayContent().getWindowCornerRadius() + : 0; adapter = new LocalAnimationAdapter( new WindowAnimationSpec(a, mTmpPoint, mTmpRect, getDisplayContent().mAppTransition.canSkipFirstFrame(), appStackClipMode, true /* isAppAnimation */, - getWindowCornerRadiusForAnimation()), + windowCornerRadius), mWmService.mSurfaceAnimationRunner); if (a.getZAdjustment() == Animation.ZORDER_TOP) { mNeedsZBoost = true; @@ -2994,8 +3004,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree pw.print(prefix); pw.print("inPendingTransaction="); pw.println(inPendingTransaction); } - if (startingData != null || removed || firstWindowDrawn || mIsExiting) { - pw.print(prefix); pw.print("startingData="); pw.print(startingData); + if (mStartingData != null || removed || firstWindowDrawn || mIsExiting) { + pw.print(prefix); pw.print("startingData="); pw.print(mStartingData); pw.print(" removed="); pw.print(removed); pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn); pw.print(" mIsExiting="); pw.println(mIsExiting); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 41292d2b232b..652f7ee48b9f 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -90,6 +90,9 @@ import static com.android.server.wm.DisplayContentProto.ROTATION; import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION; import static com.android.server.wm.DisplayContentProto.STACKS; import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER; +import static com.android.server.wm.DisplayContentProto.OPENING_APPS; +import static com.android.server.wm.DisplayContentProto.CHANGING_APPS; +import static com.android.server.wm.DisplayContentProto.CLOSING_APPS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT; @@ -144,6 +147,7 @@ import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Matrix; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; @@ -541,6 +545,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final InsetsStateController mInsetsStateController; + /** @see #getParentWindow() */ + private WindowState mParentWindow; + + private Point mLocationInParentWindow = new Point(); private SurfaceControl mParentSurfaceControl; private InputWindowHandle mPortalWindowHandle; @@ -549,6 +557,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // Last systemUiVisibility we dispatched to windows. private int mLastDispatchedSystemUiVisibility = 0; + /** Corner radius that windows should have in order to match the display. */ + private final float mWindowCornerRadius; + private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> { WindowStateAnimator winAnimator = w.mWinAnimator; final AppWindowToken atoken = w.mAppToken; @@ -909,6 +920,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (mWmService.mSystemReady) { mDisplayPolicy.systemReady(); } + mWindowCornerRadius = mDisplayPolicy.getWindowCornerRadius(); mDividerControllerLocked = new DockedStackDividerController(service, this); mPinnedStackControllerLocked = new PinnedStackController(service, this); @@ -953,6 +965,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mDisplayId; } + float getWindowCornerRadius() { + return mWindowCornerRadius; + } + WindowToken getWindowToken(IBinder binder) { return mTokenMap.get(binder); } @@ -2723,6 +2739,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (mFocusedApp != null) { mFocusedApp.writeNameToProto(proto, FOCUSED_APP); } + for (int i = mOpeningApps.size() - 1; i >= 0; i--) { + mOpeningApps.valueAt(i).mActivityRecord.writeIdentifierToProto(proto, OPENING_APPS); + } + for (int i = mClosingApps.size() - 1; i >= 0; i--) { + mClosingApps.valueAt(i).mActivityRecord.writeIdentifierToProto(proto, CLOSING_APPS); + } + for (int i = mChangingApps.size() - 1; i >= 0; i--) { + mChangingApps.valueAt(i).mActivityRecord.writeIdentifierToProto(proto, CHANGING_APPS); + } proto.end(token); } @@ -4562,7 +4587,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo token2.mOwnerCanManageAppTokens) ? -1 : 1; private final Predicate<WindowState> mGetOrientingWindow = w -> { - if (!w.isVisibleLw() || !w.mPolicyVisibilityAfterAnim) { + if (!w.isVisibleLw() || !w.mLegacyPolicyVisibilityAfterAnim) { return false; } final int req = w.mAttrs.screenOrientation; @@ -4923,11 +4948,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** * Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and - * {@link #mOverlayLayer} to the specified surfaceControl. + * {@link #mOverlayLayer} to the specified SurfaceControl. * + * @param win The window which owns the SurfaceControl. This indicates the z-order of the + * windows of this display against the windows on the parent display. * @param sc The new SurfaceControl, where the DisplayContent's surfaces will be re-parented to. */ - void reparentDisplayContent(SurfaceControl sc) { + void reparentDisplayContent(WindowState win, SurfaceControl sc) { + mParentWindow = win; mParentSurfaceControl = sc; if (mPortalWindowHandle == null) { mPortalWindowHandle = createPortalWindowHandle(sc.toString()); @@ -4936,6 +4964,41 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo .reparent(mWindowingLayer, sc).reparent(mOverlayLayer, sc); } + /** + * Get the window which owns the surface that this DisplayContent is re-parented to. + * + * @return the parent window. + */ + WindowState getParentWindow() { + return mParentWindow; + } + + /** + * Update the location of this display in the parent window. This enables windows in this + * display to compute the global transformation matrix. + * + * @param win The parent window of this display. + * @param x The x coordinate in the parent window. + * @param y The y coordinate in the parent window. + */ + void updateLocation(WindowState win, int x, int y) { + if (mParentWindow != win) { + throw new IllegalArgumentException( + "The given window is not the parent window of this display."); + } + if (mLocationInParentWindow.x != x || mLocationInParentWindow.y != y) { + mLocationInParentWindow.x = x; + mLocationInParentWindow.y = y; + if (mWmService.mAccessibilityController != null) { + mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); + } + } + } + + Point getLocationInParentWindow() { + return mLocationInParentWindow; + } + @VisibleForTesting SurfaceControl getWindowingLayer() { return mWindowingLayer; diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 3bb365338749..c23cdbba7706 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECOND import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.res.Configuration.UI_MODE_TYPE_CAR; import static android.content.res.Configuration.UI_MODE_TYPE_MASK; +import static android.view.Display.TYPE_BUILT_IN; import static android.view.InsetsState.TYPE_TOP_BAR; import static android.view.InsetsState.TYPE_TOP_GESTURES; import static android.view.InsetsState.TYPE_TOP_TAPPABLE_ELEMENT; @@ -112,7 +113,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Px; import android.app.ActivityManager; -import android.app.ActivityManagerInternal; import android.app.ActivityThread; import android.app.LoadedApk; import android.app.ResourcesManager; @@ -133,6 +133,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.util.ArraySet; +import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Slog; import android.view.DisplayCutout; @@ -157,6 +158,7 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.util.ScreenShapeHelper; import com.android.internal.util.ScreenshotHelper; import com.android.internal.util.function.TriConsumer; @@ -2718,11 +2720,18 @@ public class DisplayPolicy { private void updateCurrentUserResources() { final int userId = mService.mAmInternal.getCurrentUserId(); final Context uiContext = getSystemUiContext(); + + if (userId == UserHandle.USER_SYSTEM) { + // Skip the (expensive) recreation of resources for the system user below and just + // use the resources from the system ui context + mCurrentUserResources = uiContext.getResources(); + return; + } + + // For non-system users, ensure that the resources are loaded from the current + // user's package info (see ContextImpl.createDisplayContext) final LoadedApk pi = ActivityThread.currentActivityThread().getPackageInfo( uiContext.getPackageName(), null, 0, userId); - - // Create the resources from the current-user package info - // (see ContextImpl.createDisplayContext) mCurrentUserResources = ResourcesManager.getInstance().getResources(null, pi.getResDir(), null /* splitResDirs */, @@ -2870,6 +2879,16 @@ public class DisplayPolicy { - statusBarHeight; } + /** + * Return corner radius in pixels that should be used on windows in order to cover the display. + * The radius is only valid for built-in displays since the one who configures window corner + * radius cannot know the corner radius of non-built-in display. + */ + float getWindowCornerRadius() { + return mDisplayContent.getDisplay().getType() == TYPE_BUILT_IN + ? ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources()) : 0f; + } + boolean isShowingDreamLw() { return mShowingDream; } @@ -3113,7 +3132,9 @@ public class DisplayPolicy { WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds); mService.getStackBounds( WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds); - final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility); + final Pair<Integer, Boolean> result = + updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility); + final int visibility = result.first; final int diff = visibility ^ mLastSystemUiFlags; final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags; final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags; @@ -3133,13 +3154,14 @@ public class DisplayPolicy { mLastDockedStackBounds.set(mDockedStackBounds); final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds); final Rect dockedStackBounds = new Rect(mDockedStackBounds); + final boolean isNavbarColorManagedByIme = result.second; mHandler.post(() -> { StatusBarManagerInternal statusBar = getStatusBarManagerInternal(); if (statusBar != null) { final int displayId = getDisplayId(); statusBar.setSystemUiVisibility(displayId, visibility, fullscreenVisibility, dockedVisibility, 0xffffffff, fullscreenStackBounds, - dockedStackBounds, win.toString()); + dockedStackBounds, isNavbarColorManagedByIme, win.toString()); statusBar.topAppWindowChanged(displayId, needsMenu); } }); @@ -3222,7 +3244,7 @@ public class DisplayPolicy { return vis; } - private int updateSystemBarsLw(WindowState win, int oldVis, int vis) { + private Pair<Integer, Boolean> updateSystemBarsLw(WindowState win, int oldVis, int vis) { final boolean dockedStackVisible = mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); final boolean freeformStackVisible = @@ -3355,8 +3377,11 @@ public class DisplayPolicy { vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState, mDisplayContent.mInputMethodWindow, navColorWin); + // Navbar color is controlled by the IME. + final boolean isManagedByIme = + navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow; - return vis; + return Pair.create(vis, isManagedByIme); } private boolean drawsBarBackground(int vis, WindowState win, BarController controller, diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index f67b11b26b12..402ec5976840 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -29,12 +29,11 @@ import android.annotation.Nullable; import android.graphics.Point; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; +import android.view.InsetsSource; +import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; -import android.view.InsetsSource; -import android.view.InsetsSourceControl; -import android.view.ViewRootImpl; import com.android.internal.util.function.TriConsumer; import com.android.internal.util.function.pooled.PooledLambda; @@ -142,7 +141,7 @@ class InsetsSourceProvider { mStateController.notifyControlChanged(mControllingWin); } } - setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.mPolicyVisibility + setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.isVisibleByPolicy() && !mWin.mGivenInsetsPending); } diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index b33f8c7ad658..34273f3f23a5 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -426,6 +426,16 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } @Override + public void reparentDisplayContent(IWindow window, SurfaceControl sc, int displayId) { + mService.reparentDisplayContent(window, sc, displayId); + } + + @Override + public void updateDisplayContentLocation(IWindow window, int x, int y, int displayId) { + mService.updateDisplayContentLocation(window, x, y, displayId); + } + + @Override public void updateTapExcludeRegion(IWindow window, int regionId, Region region) { final long identity = Binder.clearCallingIdentity(); try { diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index d4d157fbe75c..8505ec2b8a82 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -1161,6 +1161,19 @@ class TaskRecord extends ConfigurationContainer { return false; } + /** + * Return true if any activities in this task belongs to input uid. + */ + boolean containsAppUid(int uid) { + for (int i = mActivities.size() - 1; i >= 0; --i) { + final ActivityRecord r = mActivities.get(i); + if (r.getUid() == uid) { + return true; + } + } + return false; + } + void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) { if (mStack != null) { for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 4eddb3016205..7ac887ed295d 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -240,7 +240,6 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.IResultReceiver; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.IShortcutService; -import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.LatencyTracker; @@ -793,9 +792,6 @@ public class WindowManagerService extends IWindowManager.Stub final DisplayManager mDisplayManager; final ActivityTaskManagerService mAtmService; - /** Corner radius that windows should have in order to match the display. */ - final float mWindowCornerRadius; - /** Indicates whether this device supports wide color gamut / HDR rendering */ private boolean mHasWideColorGamutSupport; private boolean mHasHdrSupport; @@ -1020,7 +1016,6 @@ public class WindowManagerService extends IWindowManager.Stub mInputManager = inputManager; // Must be before createDisplayContentLocked. mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); mDisplayWindowSettings = new DisplayWindowSettings(this); - mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context.getResources()); mTransactionFactory = transactionFactory; mTransaction = mTransactionFactory.make(); @@ -1880,7 +1875,8 @@ public class WindowManagerService extends IWindowManager.Stub // We need to report touchable region changes to accessibility. if (mAccessibilityController != null - && w.getDisplayContent().getDisplayId() == DEFAULT_DISPLAY) { + && (w.getDisplayContent().getDisplayId() == DEFAULT_DISPLAY + || w.getDisplayContent().getParentWindow() != null)) { mAccessibilityController.onSomeWindowResizedOrMovedLocked(); } } @@ -2012,7 +2008,8 @@ public class WindowManagerService extends IWindowManager.Stub } if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0) && (mAccessibilityController != null) - && (win.getDisplayId() == DEFAULT_DISPLAY)) { + && (win.getDisplayId() == DEFAULT_DISPLAY + || win.getDisplayContent().getParentWindow() != null)) { // No move or resize, but the controller checks for title changes as well mAccessibilityController.onSomeWindowResizedOrMovedLocked(); } @@ -5300,7 +5297,7 @@ public class WindowManagerService extends IWindowManager.Stub ": removed=" + win.mRemoved + " visible=" + win.isVisibleLw() + " mHasSurface=" + win.mHasSurface + " drawState=" + win.mWinAnimator.mDrawState); - if (win.mRemoved || !win.mHasSurface || !win.mPolicyVisibility) { + if (win.mRemoved || !win.mHasSurface || !win.isVisibleByPolicy()) { // Window has been removed or hidden; no draw will now happen, so stop waiting. if (DEBUG_SCREEN_ON) Slog.w(TAG_WM, "Aborted waiting for drawn: " + win); mWaitingForDrawn.remove(win); @@ -6703,6 +6700,61 @@ public class WindowManagerService extends IWindowManager.Stub } } + private void checkCallerOwnsDisplay(int displayId) { + final Display display = mDisplayManager.getDisplay(displayId); + if (display == null) { + throw new IllegalArgumentException( + "Cannot find display for non-existent displayId: " + displayId); + } + + final int callingUid = Binder.getCallingUid(); + final int displayOwnerUid = display.getOwnerUid(); + if (callingUid != displayOwnerUid) { + throw new SecurityException("The caller doesn't own the display."); + } + } + + /** @see Session#reparentDisplayContent(IWindow, SurfaceControl, int) */ + void reparentDisplayContent(IWindow client, SurfaceControl sc, int displayId) { + checkCallerOwnsDisplay(displayId); + + synchronized (mGlobalLock) { + final long token = Binder.clearCallingIdentity(); + try { + final WindowState win = windowForClientLocked(null, client, false); + if (win == null) { + Slog.w(TAG_WM, "Bad requesting window " + client); + return; + } + getDisplayContentOrCreate(displayId, null).reparentDisplayContent(win, sc); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + + /** @see Session#updateDisplayContentLocation(IWindow, int, int, int) */ + void updateDisplayContentLocation(IWindow client, int x, int y, int displayId) { + checkCallerOwnsDisplay(displayId); + + synchronized (mGlobalLock) { + final long token = Binder.clearCallingIdentity(); + try { + final WindowState win = windowForClientLocked(null, client, false); + if (win == null) { + Slog.w(TAG_WM, "Bad requesting window " + client); + return; + } + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.updateLocation(win, x, y); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + /** * Update a tap exclude region in the window identified by the provided id. Touches down on this * region will not: @@ -7538,31 +7590,6 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void reparentDisplayContent(int displayId, SurfaceControl sc) { - final Display display = mDisplayManager.getDisplay(displayId); - if (display == null) { - throw new IllegalArgumentException( - "Can't reparent display for non-existent displayId: " + displayId); - } - - final int callingUid = Binder.getCallingUid(); - final int displayOwnerUid = display.getOwnerUid(); - if (callingUid != displayOwnerUid) { - throw new SecurityException("Only owner of the display can reparent surfaces to it."); - } - - synchronized (mGlobalLock) { - long token = Binder.clearCallingIdentity(); - try { - DisplayContent displayContent = getDisplayContentOrCreate(displayId, null); - displayContent.reparentDisplayContent(sc); - } finally { - Binder.restoreCallingIdentity(token); - } - } - } - - @Override public boolean injectInputAfterTransactionsApplied(InputEvent ev, int mode) { boolean shouldWaitForAnimToComplete = false; if (ev instanceof KeyEvent) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index e39cd56a938c..497c91ab329d 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -251,18 +251,33 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP int mSeq; int mViewVisibility; int mSystemUiVisibility; + /** - * The visibility of the window based on policy like {@link WindowManagerPolicy}. + * The visibility flag of the window based on policy like {@link WindowManagerPolicy}. * Normally set by calling {@link #showLw} and {@link #hideLw}. + * + * TODO: b/131253938 This will eventually be split into individual visibility policy flags. + */ + static final int LEGACY_POLICY_VISIBILITY = 1; + /** + * The visibility flag that determines whether this window is visible for the current user. + */ + private static final int VISIBLE_FOR_USER = 1 << 1; + private static final int POLICY_VISIBILITY_ALL = VISIBLE_FOR_USER | LEGACY_POLICY_VISIBILITY; + /** + * The Bitwise-or of flags that contribute to visibility of the WindowState */ - boolean mPolicyVisibility = true; + private int mPolicyVisibility = POLICY_VISIBILITY_ALL; + /** - * What {@link #mPolicyVisibility} should be set to after a transition animation. - * For example, {@link #mPolicyVisibility} might true during an exit animation to hide it and - * then set to the value of {@link #mPolicyVisibilityAfterAnim} which is false after the exit - * animation is done. + * Whether {@link #LEGACY_POLICY_VISIBILITY} flag should be set after a transition animation. + * For example, {@link #LEGACY_POLICY_VISIBILITY} might be set during an exit animation to hide + * it and then unset when the value of {@link #mLegacyPolicyVisibilityAfterAnim} is false + * after the exit animation is done. + * + * TODO: b/131253938 Determine whether this can be changed to use a visibility flag instead. */ - boolean mPolicyVisibilityAfterAnim = true; + boolean mLegacyPolicyVisibilityAfterAnim = true; // overlay window is hidden because the owning app is suspended private boolean mHiddenWhileSuspended; private boolean mAppOpVisibility = true; @@ -1414,13 +1429,33 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override boolean isVisible() { - return wouldBeVisibleIfPolicyIgnored() && mPolicyVisibility + return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicy() // If we don't have a provider, this window isn't used as a window generating // insets, so nobody can hide it over the inset APIs. && (mInsetProvider == null || mInsetProvider.isClientVisible()); } /** + * Ensures that all the policy visibility bits are set. + * @return {@code true} if all flags about visiblity are set + */ + boolean isVisibleByPolicy() { + return (mPolicyVisibility & POLICY_VISIBILITY_ALL) == POLICY_VISIBILITY_ALL; + } + + void clearPolicyVisibilityFlag(int policyVisibilityFlag) { + mPolicyVisibility &= ~policyVisibilityFlag; + } + + void setPolicyVisibilityFlag(int policyVisibilityFlag) { + mPolicyVisibility |= policyVisibilityFlag; + } + + private boolean isLegacyPolicyVisibility() { + return (mPolicyVisibility & LEGACY_POLICY_VISIBILITY) != 0; + } + + /** * @return {@code true} if the window would be visible if we'd ignore policy visibility, * {@code false} otherwise. */ @@ -1470,7 +1505,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP boolean isVisibleOrAdding() { final AppWindowToken atoken = mAppToken; return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE)) - && mPolicyVisibility && !isParentWindowHidden() + && isVisibleByPolicy() && !isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested) && !mAnimatingExit && !mDestroying; } @@ -1481,7 +1516,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * being visible. */ boolean isOnScreen() { - if (!mHasSurface || mDestroying || !mPolicyVisibility) { + if (!mHasSurface || mDestroying || !isVisibleByPolicy()) { return false; } final AppWindowToken atoken = mAppToken; @@ -1522,7 +1557,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } final boolean parentAndClientVisible = !isParentWindowHidden() && mViewVisibility == View.VISIBLE && !mToken.isHidden(); - return mHasSurface && mPolicyVisibility && !mDestroying + return mHasSurface && isVisibleByPolicy() && !mDestroying && (parentAndClientVisible || isAnimating()); } @@ -1551,7 +1586,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override public boolean isDisplayedLw() { final AppWindowToken atoken = mAppToken; - return isDrawnLw() && mPolicyVisibility + return isDrawnLw() && isVisibleByPolicy() && ((!isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested)) || isAnimating()); } @@ -2057,8 +2092,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP Slog.i(TAG_WM, " mSurfaceController=" + mWinAnimator.mSurfaceController + " relayoutCalled=" + mRelayoutCalled + " viewVis=" + mViewVisibility - + " policyVis=" + mPolicyVisibility - + " policyVisAfterAnim=" + mPolicyVisibilityAfterAnim + + " policyVis=" + isVisibleByPolicy() + + " policyVisAfterAnim=" + mLegacyPolicyVisibilityAfterAnim + " parentHidden=" + isParentWindowHidden() + " exiting=" + mAnimatingExit + " destroying=" + mDestroying); if (mAppToken != null) { @@ -2192,7 +2227,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (isHiddenFromUserLocked()) { if (DEBUG_VISIBILITY) Slog.w(TAG_WM, "user changing, hiding " + this + ", attrs=" + mAttrs.type + ", belonging to " + mOwnerUid); - hideLw(false); + clearPolicyVisibilityFlag(VISIBLE_FOR_USER); + } else { + setPolicyVisibilityFlag(VISIBLE_FOR_USER); } } @@ -2284,13 +2321,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } void checkPolicyVisibilityChange() { - if (mPolicyVisibility != mPolicyVisibilityAfterAnim) { + if (isLegacyPolicyVisibility() != mLegacyPolicyVisibilityAfterAnim) { if (DEBUG_VISIBILITY) { Slog.v(TAG, "Policy visibility changing after anim in " + - mWinAnimator + ": " + mPolicyVisibilityAfterAnim); + mWinAnimator + ": " + mLegacyPolicyVisibilityAfterAnim); + } + if (mLegacyPolicyVisibilityAfterAnim) { + setPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY); + } else { + clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY); } - mPolicyVisibility = mPolicyVisibilityAfterAnim; - if (!mPolicyVisibility) { + if (!isVisibleByPolicy()) { mWinAnimator.hide("checkPolicyVisibilityChange"); if (isFocused()) { if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, @@ -2531,7 +2572,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } boolean showLw(boolean doAnimation, boolean requestAnim) { - if (mPolicyVisibility && mPolicyVisibilityAfterAnim) { + if (isLegacyPolicyVisibility() && mLegacyPolicyVisibilityAfterAnim) { // Already showing. return false; } @@ -2558,18 +2599,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this); if (doAnimation) { if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility=" - + mPolicyVisibility + " animating=" + isAnimating()); + + isLegacyPolicyVisibility() + " animating=" + isAnimating()); if (!mToken.okToAnimate()) { doAnimation = false; - } else if (mPolicyVisibility && !isAnimating()) { + } else if (isLegacyPolicyVisibility() && !isAnimating()) { // Check for the case where we are currently visible and // not animating; we do not want to do animation at such a // point to become visible when we already are. doAnimation = false; } } - mPolicyVisibility = true; - mPolicyVisibilityAfterAnim = true; + setPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY); + mLegacyPolicyVisibilityAfterAnim = true; if (doAnimation) { mWinAnimator.applyAnimationLocked(TRANSIT_ENTER, true); } @@ -2593,7 +2634,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP doAnimation = false; } } - boolean current = doAnimation ? mPolicyVisibilityAfterAnim : mPolicyVisibility; + boolean current = + doAnimation ? mLegacyPolicyVisibilityAfterAnim : isLegacyPolicyVisibility(); if (!current) { // Already hiding. return false; @@ -2604,11 +2646,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP doAnimation = false; } } - mPolicyVisibilityAfterAnim = false; + mLegacyPolicyVisibilityAfterAnim = false; final boolean isFocused = isFocused(); if (!doAnimation) { if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this); - mPolicyVisibility = false; + clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY); // Window is no longer visible -- make sure if we were waiting // for it to be displayed before enabling the display, that // we allow the display to be enabled now. @@ -3100,7 +3142,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } //TODO (multidisplay): Accessibility supported only for the default display. - if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) { + if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY + || getDisplayContent().getParentWindow() != null)) { mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); } @@ -3443,11 +3486,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP pw.println(prefix + "mSeq=" + mSeq + " mSystemUiVisibility=0x" + Integer.toHexString(mSystemUiVisibility)); } - if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || !mAppOpVisibility + if (!isVisibleByPolicy() || !mLegacyPolicyVisibilityAfterAnim || !mAppOpVisibility || isParentWindowHidden() || mPermanentlyHidden || mForceHideNonSystemOverlayWindow || mHiddenWhileSuspended) { - pw.println(prefix + "mPolicyVisibility=" + mPolicyVisibility - + " mPolicyVisibilityAfterAnim=" + mPolicyVisibilityAfterAnim + pw.println(prefix + "mPolicyVisibility=" + isVisibleByPolicy() + + " mLegacyPolicyVisibilityAfterAnim=" + mLegacyPolicyVisibilityAfterAnim + " mAppOpVisibility=" + mAppOpVisibility + " parentHidden=" + isParentWindowHidden() + " mPermanentlyHidden=" + mPermanentlyHidden @@ -3904,7 +3947,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + ": mDrawState=" + mWinAnimator.drawStateToString() + " readyForDisplay=" + isReadyForDisplay() + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING) - + " during animation: policyVis=" + mPolicyVisibility + + " during animation: policyVis=" + isVisibleByPolicy() + " parentHidden=" + isParentWindowHidden() + " tok.hiddenRequested=" + (mAppToken != null && mAppToken.hiddenRequested) @@ -4165,7 +4208,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } //TODO (multidisplay): Accessibility is supported only for the default display. - if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) { + if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY + || getDisplayContent().getParentWindow() != null)) { mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); } @@ -4313,7 +4357,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + ", animating=" + isAnimating()); if (!isDrawnLw()) { Slog.v(TAG, "Not displayed: s=" + mWinAnimator.mSurfaceController - + " pv=" + mPolicyVisibility + + " pv=" + isVisibleByPolicy() + " mDrawState=" + mWinAnimator.mDrawState + " ph=" + isParentWindowHidden() + " th=" + (mAppToken != null ? mAppToken.hiddenRequested : false) @@ -4555,7 +4599,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked()); final AnimationAdapter adapter = new LocalAnimationAdapter( new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */, - mToken.getWindowCornerRadiusForAnimation()), + 0 /* windowCornerRadius */), mWmService.mSurfaceAnimationRunner); startAnimation(mPendingTransaction, adapter); commitPendingTransaction(); @@ -4604,6 +4648,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP int x = mSurfacePosition.x; int y = mSurfacePosition.y; + // We might be on a display which has been re-parented to a view in another window, so here + // computes the global location of our display. + DisplayContent dc = getDisplayContent(); + while (dc != null && dc.getParentWindow() != null) { + final WindowState displayParent = dc.getParentWindow(); + x += displayParent.mWindowFrames.mFrame.left - displayParent.mAttrs.surfaceInsets.left + + (dc.getLocationInParentWindow().x * displayParent.mGlobalScale + 0.5f); + y += displayParent.mWindowFrames.mFrame.top - displayParent.mAttrs.surfaceInsets.top + + (dc.getLocationInParentWindow().y * displayParent.mGlobalScale + 0.5f); + dc = displayParent.getDisplayContent(); + } + // If changed, also adjust transformFrameToSurfacePosition final WindowContainer parent = getParent(); if (isChildWindow()) { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 780d4714275b..20e1ac6c913d 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -256,7 +256,7 @@ class WindowStateAnimator { mWin.checkPolicyVisibilityChange(); final DisplayContent displayContent = mWin.getDisplayContent(); - if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) { + if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.isVisibleByPolicy()) { // Upon completion of a not-visible to visible status bar animation a relayout is // required. if (displayContent != null) { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index f65f0ab62f69..f0b9c62f2843 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -345,8 +345,4 @@ class WindowToken extends WindowContainer<WindowState> { mOwnerCanManageAppTokens); return mOwnerCanManageAppTokens && (layer > navLayer); } - - float getWindowCornerRadiusForAnimation() { - return mDisplayContent.isDefaultDisplay ? mWmService.mWindowCornerRadius : 0; - } } diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 4d37f1ace9c0..98c620c50f54 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -45,6 +45,13 @@ #include <string.h> #include <utils/SystemClock.h> +static jclass class_gnssMeasurementsEvent; +static jclass class_gnssMeasurement; +static jclass class_location; +static jclass class_gnssNavigationMessage; +static jclass class_gnssClock; +static jclass class_gnssConfiguration_halInterfaceVersion; + static jobject mCallbacksObj = nullptr; static jmethodID method_reportLocation; @@ -95,6 +102,12 @@ static jmethodID method_correctionPlaneAltDeg; static jmethodID method_correctionPlaneAzimDeg; static jmethodID method_reportNfwNotification; static jmethodID method_isInEmergencySession; +static jmethodID method_gnssMeasurementsEventCtor; +static jmethodID method_locationCtor; +static jmethodID method_gnssNavigationMessageCtor; +static jmethodID method_gnssClockCtor; +static jmethodID method_gnssMeasurementCtor; +static jmethodID method_halInterfaceVersionCtor; /* * Save a pointer to JavaVm to attach/detach threads executing @@ -255,11 +268,11 @@ void JavaMethodHelper<T>::callJavaMethod( class JavaObject { public: - JavaObject(JNIEnv* env, const char* class_name); - JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1); - JavaObject(JNIEnv* env, const char* class_name, jobject object); + JavaObject(JNIEnv* env, jclass clazz, jmethodID defaultCtor); + JavaObject(JNIEnv* env, jclass clazz, jmethodID stringCtor, const char * sz_arg_1); + JavaObject(JNIEnv* env, jclass clazz, jobject object); - virtual ~JavaObject(); + virtual ~JavaObject() = default; template<class T> void callSetter(const char* method_name, T value); @@ -273,25 +286,20 @@ class JavaObject { jobject object_; }; -JavaObject::JavaObject(JNIEnv* env, const char* class_name) : env_(env) { - clazz_ = env_->FindClass(class_name); - jmethodID ctor = env->GetMethodID(clazz_, "<init>", "()V"); - object_ = env_->NewObject(clazz_, ctor); +JavaObject::JavaObject(JNIEnv* env, jclass clazz, jmethodID defaultCtor) : env_(env), + clazz_(clazz) { + object_ = env_->NewObject(clazz_, defaultCtor); } -JavaObject::JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1) : env_(env) { - clazz_ = env_->FindClass(class_name); - jmethodID ctor = env->GetMethodID(clazz_, "<init>", "(Ljava/lang/String;)V"); - object_ = env_->NewObject(clazz_, ctor, env->NewStringUTF(sz_arg_1)); -} -JavaObject::JavaObject(JNIEnv* env, const char* class_name, jobject object) - : env_(env), object_(object) { - clazz_ = env_->FindClass(class_name); +JavaObject::JavaObject(JNIEnv* env, jclass clazz, jmethodID stringCtor, const char * sz_arg_1) + : env_(env), clazz_(clazz) { + object_ = env_->NewObject(clazz_, stringCtor, env->NewStringUTF(sz_arg_1)); } -JavaObject::~JavaObject() { - env_->DeleteLocalRef(clazz_); + +JavaObject::JavaObject(JNIEnv* env, jclass clazz, jobject object) + : env_(env), clazz_(clazz), object_(object) { } template<class T> @@ -358,11 +366,8 @@ static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodNa } static jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) { - jclass versionClass = - env->FindClass("com/android/server/location/GnssConfiguration$HalInterfaceVersion"); - jmethodID versionCtor = env->GetMethodID(versionClass, "<init>", "(II)V"); - jobject version = env->NewObject(versionClass, versionCtor, major, minor); - env->DeleteLocalRef(versionClass); + jobject version = env->NewObject(class_gnssConfiguration_halInterfaceVersion, + method_halInterfaceVersionCtor, major, minor); return version; } @@ -452,7 +457,7 @@ static JNIEnv* getJniEnv() { static jobject translateGnssLocation(JNIEnv* env, const GnssLocation_V1_0& location) { - JavaObject object(env, "android/location/Location", "gps"); + JavaObject object(env, class_location, method_locationCtor, "gps"); uint16_t flags = static_cast<uint32_t>(location.gnssLocationFlags); if (flags & GnssLocationFlags::HAS_LAT_LONG) { @@ -488,8 +493,7 @@ static jobject translateGnssLocation(JNIEnv* env, static jobject translateGnssLocation(JNIEnv* env, const GnssLocation_V2_0& location) { - JavaObject object(env, "android/location/Location", - translateGnssLocation(env, location.v1_0)); + JavaObject object(env, class_location, translateGnssLocation(env, location.v1_0)); const uint16_t flags = static_cast<uint16_t>(location.elapsedRealtime.flags); @@ -946,7 +950,7 @@ Return<void> GnssNavigationMessageCallback::gnssNavigationMessageCb( return Void(); } - JavaObject object(env, "android/location/GnssNavigationMessage"); + JavaObject object(env, class_gnssNavigationMessage, method_gnssNavigationMessageCtor); SET(Type, static_cast<int32_t>(message.type)); SET(Svid, static_cast<int32_t>(message.svid)); SET(MessageId, static_cast<int32_t>(message.messageId)); @@ -1013,7 +1017,7 @@ template<class T> void GnssMeasurementCallback::translateAndSetGnssData(const T& data) { JNIEnv* env = getJniEnv(); - JavaObject gnssClockJavaObject(env, "android/location/GnssClock"); + JavaObject gnssClockJavaObject(env, class_gnssClock, method_gnssClockCtor); translateGnssClock(gnssClockJavaObject, data); jobject clock = gnssClockJavaObject.get(); @@ -1175,41 +1179,30 @@ jobjectArray GnssMeasurementCallback::translateAllGnssMeasurements(JNIEnv* env, return nullptr; } - jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement"); jobjectArray gnssMeasurementArray = env->NewObjectArray( count, - gnssMeasurementClass, + class_gnssMeasurement, nullptr /* initialElement */); for (uint16_t i = 0; i < count; ++i) { - JavaObject object(env, "android/location/GnssMeasurement"); + JavaObject object(env, class_gnssMeasurement, method_gnssMeasurementCtor); translateSingleGnssMeasurement(&(measurements[i]), object); env->SetObjectArrayElement(gnssMeasurementArray, i, object.get()); } - env->DeleteLocalRef(gnssMeasurementClass); return gnssMeasurementArray; } void GnssMeasurementCallback::setMeasurementData(JNIEnv* env, jobject clock, jobjectArray measurementArray) { - jclass gnssMeasurementsEventClass = - env->FindClass("android/location/GnssMeasurementsEvent"); - jmethodID gnssMeasurementsEventCtor = - env->GetMethodID( - gnssMeasurementsEventClass, - "<init>", - "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V"); - - jobject gnssMeasurementsEvent = env->NewObject(gnssMeasurementsEventClass, - gnssMeasurementsEventCtor, + jobject gnssMeasurementsEvent = env->NewObject(class_gnssMeasurementsEvent, + method_gnssMeasurementsEventCtor, clock, measurementArray); env->CallVoidMethod(mCallbacksObj, method_reportMeasurementData, gnssMeasurementsEvent); checkAndClearExceptionFromCallback(env, __FUNCTION__); - env->DeleteLocalRef(gnssMeasurementsEventClass); env->DeleteLocalRef(gnssMeasurementsEvent); } @@ -1464,8 +1457,7 @@ template<class T> Return<void> GnssBatchingCallbackUtil::gnssLocationBatchCbImpl(const hidl_vec<T>& locations) { JNIEnv* env = getJniEnv(); - jobjectArray jLocations = env->NewObjectArray(locations.size(), - env->FindClass("android/location/Location"), nullptr); + jobjectArray jLocations = env->NewObjectArray(locations.size(), class_location, nullptr); for (uint16_t i = 0; i < locations.size(); ++i) { jobject jLocation = translateGnssLocation(env, locations[i]); @@ -1617,6 +1609,36 @@ static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass method_correctionPlaneAltDeg = env->GetMethodID(refPlaneClass, "getAltitudeMeters", "()D"); method_correctionPlaneAzimDeg = env->GetMethodID(refPlaneClass, "getAzimuthDegrees", "()D"); + jclass gnssMeasurementsEventClass = env->FindClass("android/location/GnssMeasurementsEvent"); + class_gnssMeasurementsEvent= (jclass) env->NewGlobalRef(gnssMeasurementsEventClass); + method_gnssMeasurementsEventCtor = env->GetMethodID( + class_gnssMeasurementsEvent, + "<init>", + "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V"); + + jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement"); + class_gnssMeasurement = (jclass) env->NewGlobalRef(gnssMeasurementClass); + method_gnssMeasurementCtor = env->GetMethodID(class_gnssMeasurement, "<init>", "()V"); + + jclass locationClass = env->FindClass("android/location/Location"); + class_location = (jclass) env->NewGlobalRef(locationClass); + method_locationCtor = env->GetMethodID(class_location, "<init>", "(Ljava/lang/String;)V"); + + jclass gnssNavigationMessageClass = env->FindClass("android/location/GnssNavigationMessage"); + class_gnssNavigationMessage = (jclass) env->NewGlobalRef(gnssNavigationMessageClass); + method_gnssNavigationMessageCtor = env->GetMethodID(class_gnssNavigationMessage, "<init>", "()V"); + + jclass gnssClockClass = env->FindClass("android/location/GnssClock"); + class_gnssClock = (jclass) env->NewGlobalRef(gnssClockClass); + method_gnssClockCtor = env->GetMethodID(class_gnssClock, "<init>", "()V"); + + jclass gnssConfiguration_halInterfaceVersionClass = + env->FindClass("com/android/server/location/GnssConfiguration$HalInterfaceVersion"); + class_gnssConfiguration_halInterfaceVersion = + (jclass) env->NewGlobalRef(gnssConfiguration_halInterfaceVersionClass); + method_halInterfaceVersionCtor = + env->GetMethodID(class_gnssConfiguration_halInterfaceVersion, "<init>", "(II)V"); + /* * Save a pointer to JVM. */ diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index c5a206882695..22231c0ab09b 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -8034,6 +8034,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { throw new IllegalArgumentException("Component " + who + " not installed for userId:" + userHandle); } + final boolean hasIncompatibleAccountsOrNonAdb = hasIncompatibleAccountsOrNonAdbNoLock(userHandle, who); synchronized (getLockObject()) { @@ -8539,9 +8540,30 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } enforceCanManageProfileAndDeviceOwners(); - if ((mIsWatch || hasUserSetupCompleted(userHandle)) && !isCallerWithSystemUid()) { - throw new IllegalStateException("Cannot set the profile owner on a user which is " - + "already set-up"); + + if ((mIsWatch || hasUserSetupCompleted(userHandle))) { + if (!isCallerWithSystemUid()) { + throw new IllegalStateException("Cannot set the profile owner on a user which is " + + "already set-up"); + } + + if (!mIsWatch) { + // Only the default supervision profile owner can be set as profile owner after SUW + final String supervisor = mContext.getResources().getString( + com.android.internal.R.string + .config_defaultSupervisionProfileOwnerComponent); + if (supervisor == null) { + throw new IllegalStateException("Unable to set profile owner post-setup, no" + + "default supervisor profile owner defined"); + } + + final ComponentName supervisorComponent = ComponentName.unflattenFromString( + supervisor); + if (!owner.equals(supervisorComponent)) { + throw new IllegalStateException("Unable to set non-default profile owner" + + " post-setup " + owner); + } + } } } diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 6ec864c05258..f4a6231e43fb 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -16,6 +16,7 @@ package com.android.server.usage; +import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START; import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN; import static android.app.usage.UsageEvents.Event.SLICE_PINNED; import static android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV; @@ -821,6 +822,41 @@ public class AppStandbyControllerTests { } @Test + public void testInitialForegroundServiceTimeout() throws Exception { + setChargingState(mController, false); + + mInjector.mElapsedRealtime = 1 * RARE_THRESHOLD + 100; + // Make sure app is in NEVER bucket + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER, + REASON_MAIN_FORCED, mInjector.mElapsedRealtime); + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_NEVER); + + mInjector.mElapsedRealtime += 100; + + // Trigger a FOREGROUND_SERVICE_START and verify bucket + reportEvent(mController, FOREGROUND_SERVICE_START, mInjector.mElapsedRealtime, PACKAGE_1); + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_ACTIVE); + + // Verify it's still in ACTIVE close to end of timeout + mInjector.mElapsedRealtime += mController.mInitialForegroundServiceStartTimeoutMillis - 100; + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_ACTIVE); + + // Verify bucket moves to RARE after timeout + mInjector.mElapsedRealtime += 200; + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_RARE); + + // Trigger a FOREGROUND_SERVICE_START again + reportEvent(mController, FOREGROUND_SERVICE_START, mInjector.mElapsedRealtime, PACKAGE_1); + mController.checkIdleStates(USER_ID); + // Bucket should not be immediately elevated on subsequent service starts + assertBucket(STANDBY_BUCKET_RARE); + } + + @Test public void testPredictionNotOverridden() throws Exception { reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1); assertBucket(STANDBY_BUCKET_ACTIVE); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index cbca087872d9..8d56bc400d14 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -62,6 +62,7 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -85,6 +86,7 @@ import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Person; +import android.app.RemoteInput; import android.app.admin.DevicePolicyManagerInternal; import android.app.usage.UsageStatsManagerInternal; import android.companion.ICompanionDeviceManager; @@ -96,6 +98,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; +import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Color; import android.graphics.drawable.Icon; @@ -327,6 +330,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { LocalServices.removeServiceForTest(WindowManagerInternal.class); LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal); + doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any()); + mService = new TestableNotificationManagerService(mContext); // Use this testable looper. @@ -375,20 +380,16 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true); - try { - mService.init(mTestableLooper.getLooper(), - mPackageManager, mPackageManagerClient, mockLightsManager, - mListeners, mAssistants, mConditionProviders, - mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, - mGroupHelper, mAm, mAppUsageStats, - mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, - mAppOpsManager, mUm); - mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + + mService.init(mTestableLooper.getLooper(), + mPackageManager, mPackageManagerClient, mockLightsManager, + mListeners, mAssistants, mConditionProviders, + mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, + mGroupHelper, mAm, mAppUsageStats, + mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, + mAppOpsManager, mUm); + mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); + mService.setAudioManager(mAudioManager); // Tests call directly into the Binder. @@ -2080,14 +2081,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testSetListenerAccessForUser() throws Exception { UserHandle user = UserHandle.of(10); ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationListenerAccessGrantedForUser( - c, user.getIdentifier(), true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + mBinderService.setNotificationListenerAccessGrantedForUser(c, user.getIdentifier(), true); + verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); verify(mListeners, times(1)).setPackageOrComponentEnabled( @@ -2101,15 +2096,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testSetAssistantAccessForUser() throws Exception { UserHandle user = UserHandle.of(10); + List<UserInfo> uis = new ArrayList<>(); + UserInfo ui = new UserInfo(); + ui.id = 10; + uis.add(ui); ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationAssistantAccessGrantedForUser( - c, user.getIdentifier(), true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + when(mUm.getEnabledProfiles(10)).thenReturn(uis); + + mBinderService.setNotificationAssistantAccessGrantedForUser(c, user.getIdentifier(), true); verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); verify(mAssistants, times(1)).setPackageOrComponentEnabled( @@ -2150,14 +2144,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testSetDndAccessForUser() throws Exception { UserHandle user = UserHandle.of(10); ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationPolicyAccessGrantedForUser( - c.getPackageName(), user.getIdentifier(), true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + mBinderService.setNotificationPolicyAccessGrantedForUser( + c.getPackageName(), user.getIdentifier(), true); verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( @@ -2171,13 +2159,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testSetListenerAccess() throws Exception { ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationListenerAccessGranted(c, true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + mBinderService.setNotificationListenerAccessGranted(c, true); verify(mListeners, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, true, true); @@ -2189,19 +2171,45 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testSetAssistantAccess() throws Exception { + List<UserInfo> uis = new ArrayList<>(); + UserInfo ui = new UserInfo(); + ui.id = 0; + uis.add(ui); + when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationAssistantAccessGranted(c, true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + + mBinderService.setNotificationAssistantAccessGranted(c, true); + + verify(mAssistants, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), 0, true, true); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), 0, false, true); + verify(mListeners, never()).setPackageOrComponentEnabled( + any(), anyInt(), anyBoolean(), anyBoolean()); + } + + @Test + public void testSetAssistantAccess_multiProfile() throws Exception { + List<UserInfo> uis = new ArrayList<>(); + UserInfo ui = new UserInfo(); + ui.id = 0; + uis.add(ui); + UserInfo ui10 = new UserInfo(); + ui10.id = 10; + uis.add(ui10); + when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); + ComponentName c = ComponentName.unflattenFromString("package/Component"); + + mBinderService.setNotificationAssistantAccessGranted(c, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, true, true); + verify(mAssistants, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), 10, true, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, false, true); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), 10, false, true); verify(mListeners, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -2212,14 +2220,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { ComponentName c = ComponentName.unflattenFromString("package/Component"); componentList.add(c); when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList); + List<UserInfo> uis = new ArrayList<>(); + UserInfo ui = new UserInfo(); + ui.id = 0; + uis.add(ui); + when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); - try { - mBinderService.setNotificationAssistantAccessGranted(null, true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + mBinderService.setNotificationAssistantAccessGranted(null, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, true, false); @@ -2231,25 +2238,59 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testSetAssistantAccessForUser_nullWithAllowedAssistant() throws Exception { - UserHandle user = UserHandle.of(10); + List<UserInfo> uis = new ArrayList<>(); + UserInfo ui = new UserInfo(); + ui.id = 10; + uis.add(ui); + UserHandle user = ui.getUserHandle(); ArrayList<ComponentName> componentList = new ArrayList<>(); ComponentName c = ComponentName.unflattenFromString("package/Component"); componentList.add(c); when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList); + when(mUm.getEnabledProfiles(10)).thenReturn(uis); - try { - mBinderService.setNotificationAssistantAccessGrantedForUser( + mBinderService.setNotificationAssistantAccessGrantedForUser( + null, user.getIdentifier(), true); + + verify(mAssistants, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), user.getIdentifier(), true, false); + verify(mAssistants).setUserSet(10, true); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), user.getIdentifier(), false, false); + verify(mListeners, never()).setPackageOrComponentEnabled( + any(), anyInt(), anyBoolean(), anyBoolean()); + } + + @Test + public void testSetAssistantAccessForUser_workProfile_nullWithAllowedAssistant() + throws Exception { + List<UserInfo> uis = new ArrayList<>(); + UserInfo ui = new UserInfo(); + ui.id = 0; + uis.add(ui); + UserInfo ui10 = new UserInfo(); + ui10.id = 10; + uis.add(ui10); + UserHandle user = ui.getUserHandle(); + ArrayList<ComponentName> componentList = new ArrayList<>(); + ComponentName c = ComponentName.unflattenFromString("package/Component"); + componentList.add(c); + when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList); + when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); + + mBinderService.setNotificationAssistantAccessGrantedForUser( null, user.getIdentifier(), true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } verify(mAssistants, times(1)).setPackageOrComponentEnabled( c.flattenToString(), user.getIdentifier(), true, false); + verify(mAssistants, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), ui10.id, true, false); + verify(mAssistants).setUserSet(0, true); + verify(mAssistants).setUserSet(10, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), user.getIdentifier(), false, false); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), ui10.id, false, false); verify(mListeners, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -2257,13 +2298,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testSetDndAccess() throws Exception { ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + + mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.getPackageName(), 0, true, true); @@ -2291,6 +2327,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testSetAssistantAccess_doesNothingOnLowRam() throws Exception { when(mActivityManager.isLowRamDevice()).thenReturn(true); ComponentName c = ComponentName.unflattenFromString("package/Component"); + List<UserInfo> uis = new ArrayList<>(); + UserInfo ui = new UserInfo(); + ui.id = 0; + uis.add(ui); + when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); + mBinderService.setNotificationAssistantAccessGranted(c, true); verify(mListeners, never()).setPackageOrComponentEnabled( @@ -2320,13 +2362,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true); when(mActivityManager.isLowRamDevice()).thenReturn(true); ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationListenerAccessGranted(c, true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + + mBinderService.setNotificationListenerAccessGranted(c, true); verify(mListeners, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, true, true); @@ -2341,13 +2378,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true); when(mActivityManager.isLowRamDevice()).thenReturn(true); ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationAssistantAccessGranted(c, true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + List<UserInfo> uis = new ArrayList<>(); + UserInfo ui = new UserInfo(); + ui.id = 0; + uis.add(ui); + when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); + + mBinderService.setNotificationAssistantAccessGranted(c, true); verify(mListeners, never()).setPackageOrComponentEnabled( anyString(), anyInt(), anyBoolean(), anyBoolean()); @@ -2362,13 +2399,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true); when(mActivityManager.isLowRamDevice()).thenReturn(true); ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + + mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true); verify(mListeners, never()).setPackageOrComponentEnabled( anyString(), anyInt(), anyBoolean(), anyBoolean()); @@ -4492,6 +4524,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { Person person = new Person.Builder() .setName("bubblebot") .build(); + // It needs remote input to be bubble-able + RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build(); + PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); + Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon); + Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply", + inputIntent).addRemoteInput(remoteInput) + .build(); // Make it messaging style Notification.Builder nb = new Notification.Builder(mContext, mTestNotificationChannel.getId()) @@ -4504,6 +4543,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .addMessage("Is it me you're looking for?", SystemClock.currentThreadTimeMillis(), person) ) + .setActions(replyAction) .setSmallIcon(android.R.drawable.sym_def_app_icon); StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0, diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 45d52195c5fd..5803385e13e2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -79,7 +79,6 @@ import android.view.Gravity; import androidx.test.filters.SmallTest; import com.android.server.wm.LaunchParamsController.LaunchParamsModifier; -import com.android.server.wm.TaskRecord.TaskRecordFactory; import org.junit.Before; import org.junit.Test; @@ -330,21 +329,14 @@ public class ActivityStarterTests extends ActivityTestsBase { any(), any(), any(), anyInt(), anyInt(), anyInt(), any(), anyBoolean(), anyBoolean(), any(), any(), any()); - // instrument the stack and task used. - final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final TaskRecord task = new TaskBuilder(mSupervisor) - .setCreateStack(false) - .build(); - - // use factory that only returns spy task. - final TaskRecordFactory factory = mock(TaskRecordFactory.class); - TaskRecord.setTaskRecordFactory(factory); - - // return task when created. - doReturn(task).when(factory).create(any(), anyInt(), any(), any(), any(), any()); + // Use factory that only returns spy task. + mockTaskRecordFactory(); if (mockGetLaunchStack) { + // Instrument the stack and task used. + final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + // Direct starter to use spy stack. doReturn(stack).when(mRootActivityContainer) .getLaunchStack(any(), any(), any(), anyBoolean()); @@ -707,6 +699,36 @@ public class ActivityStarterTests extends ActivityTestsBase { } /** + * This test ensures that {@link ActivityStarter#setTargetStackAndMoveToFrontIfNeeded} will + * move the existing task to front if the current focused stack doesn't have running task. + */ + @Test + public void testBringTaskToFrontWhenFocusedStackIsFinising() { + // Put 2 tasks in the same stack (simulate the behavior of home stack). + final ActivityRecord activity = new ActivityBuilder(mService) + .setCreateTask(true).build(); + new ActivityBuilder(mService) + .setStack(activity.getActivityStack()) + .setCreateTask(true).build(); + + // Create a top finishing activity. + final ActivityRecord finishingTopActivity = new ActivityBuilder(mService) + .setCreateTask(true).build(); + finishingTopActivity.getActivityStack().moveToFront("finishingTopActivity"); + + assertEquals(finishingTopActivity, mRootActivityContainer.topRunningActivity()); + finishingTopActivity.finishing = true; + + // Launch the bottom task of the target stack. + prepareStarter(FLAG_ACTIVITY_NEW_TASK, false /* mockGetLaunchStack */) + .setReason("testBringTaskToFrontWhenTopStackIsFinising") + .setIntent(activity.intent) + .execute(); + // The hierarchies of the activity should move to front. + assertEquals(activity, mRootActivityContainer.topRunningActivity()); + } + + /** * This test ensures that when starting an existing single task activity on secondary display * which is not the top focused display, it should deliver new intent to the activity and not * create a new stack. diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index afd4ec160aad..09b511a20419 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -41,6 +41,7 @@ import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; import android.app.ActivityManagerInternal; import android.app.ActivityOptions; +import android.app.AppOpsManager; import android.app.IApplicationThread; import android.content.ComponentName; import android.content.Context; @@ -71,6 +72,7 @@ import com.android.server.am.PendingIntentController; import com.android.server.appop.AppOpsService; import com.android.server.firewall.IntentFirewall; import com.android.server.uri.UriGrantsManagerInternal; +import com.android.server.wm.TaskRecord.TaskRecordFactory; import com.android.server.wm.utils.MockTracker; import org.junit.After; @@ -159,6 +161,19 @@ class ActivityTestsBase { } /** + * Delegates task creation to {@link #TaskBuilder} to avoid the dependency of window hierarchy + * when starting activity in unit tests. + */ + void mockTaskRecordFactory() { + final TaskRecord task = new TaskBuilder(mSupervisor).setCreateStack(false).build(); + final TaskRecordFactory factory = mock(TaskRecordFactory.class); + TaskRecord.setTaskRecordFactory(factory); + doReturn(task).when(factory).create(any() /* service */, anyInt() /* taskId */, + any() /* info */, any() /* intent */, any() /* voiceSession */, + any() /* voiceInteractor */); + } + + /** * Builder for creating new activities. */ protected static class ActivityBuilder { @@ -413,12 +428,18 @@ class ActivityTestsBase { ActivityStackSupervisor mTestStackSupervisor; ActivityDisplay mDefaultDisplay; + AppOpsService mAppOpsService; TestActivityTaskManagerService(Context context) { super(context); spyOn(this); mUgmInternal = mock(UriGrantsManagerInternal.class); + mAppOpsService = mock(AppOpsService.class); + + // Make sure permission checks aren't overridden. + doReturn(AppOpsManager.MODE_DEFAULT) + .when(mAppOpsService).noteOperation(anyInt(), anyInt(), anyString()); mSupportsMultiWindow = true; mSupportsMultiDisplay = true; @@ -482,6 +503,11 @@ class ActivityTestsBase { } @Override + AppOpsService getAppOpsService() { + return mAppOpsService; + } + + @Override void updateCpuStats() { } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index 2d1dd39db83e..20379a2427be 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -105,6 +105,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testTransitWithinTask() { synchronized (mWm.mGlobalLock) { final AppWindowToken opening = createAppWindowToken(mDisplayContent, diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index 9cdea9a3eab4..5fc7f44588e1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -65,6 +65,7 @@ public class AppTransitionTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testKeyguardOverride() { mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); @@ -72,6 +73,7 @@ public class AppTransitionTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testKeyguardKeep() { mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); @@ -172,6 +174,7 @@ public class AppTransitionTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testLoadAnimationSafely() { DisplayContent dc = createNewDisplay(Display.STATE_ON); assertNull(dc.mAppTransition.loadAnimationSafely( diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java index 623559e41256..70d9b5fedbcb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java @@ -30,6 +30,7 @@ import static org.mockito.ArgumentMatchers.intThat; import android.platform.test.annotations.Presubmit; import android.view.SurfaceControl; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import com.android.server.wm.WindowTestUtils.TestAppWindowToken; @@ -65,6 +66,7 @@ public class AppWindowTokenAnimationTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void clipAfterAnim_boundsLayerIsCreated() { mToken.mNeedsAnimationBoundsLayer = true; @@ -86,6 +88,7 @@ public class AppWindowTokenAnimationTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void clipAfterAnim_boundsLayerIsDestroyed() { mToken.mNeedsAnimationBoundsLayer = true; mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); @@ -116,6 +119,7 @@ public class AppWindowTokenAnimationTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void clipNoneAnim_boundsLayerIsNotCreated() { mToken.mNeedsAnimationBoundsLayer = false; diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index 064b553bb314..fe4541183038 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -150,6 +150,7 @@ public class AppWindowTokenTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testLandscapeSeascapeRotationByApp() { // Some plumbing to get the service ready for rotation updates. mWm.mDisplayReady = true; @@ -504,14 +505,14 @@ public class AppWindowTokenTests extends WindowTestsBase { private void assertHasStartingWindow(AppWindowToken atoken) { assertNotNull(atoken.startingSurface); - assertNotNull(atoken.startingData); + assertNotNull(atoken.mStartingData); assertNotNull(atoken.startingWindow); } private void assertNoStartingWindow(AppWindowToken atoken) { assertNull(atoken.startingSurface); assertNull(atoken.startingWindow); - assertNull(atoken.startingData); + assertNull(atoken.mStartingData); atoken.forAllWindows(windowState -> { assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING); }, true); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index bac1ecd5ec31..b2084f88bc43 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -25,7 +25,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; import static android.view.Display.DEFAULT_DISPLAY; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; @@ -71,10 +70,10 @@ import java.util.Arrays; import java.util.List; /** - * Tests for the {@link ActivityStackSupervisor} class. + * Tests for the {@link RootActivityContainer} class. * * Build/Install/Run: - * atest WmTests:ActivityStackSupervisorTests + * atest WmTests:RootActivityContainerTests */ @MediumTest @Presubmit @@ -399,17 +398,15 @@ public class RootActivityContainerTests extends ActivityTestsBase { */ @Test public void testStartHomeOnAllDisplays() { + mockResolveHomeActivity(); + // Create secondary displays. final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay()); mRootActivityContainer.addChild(secondDisplay, POSITION_TOP); doReturn(true).when(secondDisplay).supportsSystemDecorations(); // Create mock tasks and other necessary mocks. - TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false); - final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class); - TaskRecord.setTaskRecordFactory(factory); - doAnswer(i -> taskBuilder.build()).when(factory) - .create(any(), anyInt(), any(), any(), any(), any()); + mockTaskRecordFactory(); doReturn(true).when(mRootActivityContainer) .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean()); doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay( @@ -510,6 +507,26 @@ public class RootActivityContainerTests extends ActivityTestsBase { } /** + * Tests that when starting {@link #ResolverActivity} for home, it should use the standard + * activity type (in a new stack) so the order of back stack won't be broken. + */ + @Test + public void testStartResolverActivityForHome() { + final ActivityInfo info = new ActivityInfo(); + info.applicationInfo = new ApplicationInfo(); + info.applicationInfo.packageName = "android"; + info.name = ResolverActivity.class.getName(); + doReturn(info).when(mRootActivityContainer).resolveHomeActivity(anyInt(), any()); + mockTaskRecordFactory(); + + mRootActivityContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY); + final ActivityRecord resolverActivity = mRootActivityContainer.topRunningActivity(); + + assertEquals(info, resolverActivity.info); + assertEquals(ACTIVITY_TYPE_STANDARD, resolverActivity.getActivityStack().getActivityType()); + } + + /** * Tests that secondary home should be selected if default home not set. */ @Test @@ -542,13 +559,7 @@ public class RootActivityContainerTests extends ActivityTestsBase { */ @Test public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() { - final Intent defaultHomeIntent = mService.getHomeIntent(); - final ActivityInfo aInfoDefault = new ActivityInfo(); - aInfoDefault.name = "fakeHomeActivity"; - aInfoDefault.applicationInfo = new ApplicationInfo(); - aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; - doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(), - refEq(defaultHomeIntent)); + mockResolveHomeActivity(); final List<ResolveInfo> resolutions = new ArrayList<>(); doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any()); @@ -575,13 +586,7 @@ public class RootActivityContainerTests extends ActivityTestsBase { */ @Test public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() { - final Intent homeIntent = mService.getHomeIntent(); - final ActivityInfo aInfoDefault = new ActivityInfo(); - aInfoDefault.name = "fakeHomeActivity"; - aInfoDefault.applicationInfo = new ApplicationInfo(); - aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; - doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(), - refEq(homeIntent)); + final ActivityInfo aInfoDefault = mockResolveHomeActivity(); final List<ResolveInfo> resolutions = new ArrayList<>(); final ResolveInfo infoFake1 = new ResolveInfo(); @@ -612,13 +617,7 @@ public class RootActivityContainerTests extends ActivityTestsBase { */ @Test public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() { - final Intent homeIntent = mService.getHomeIntent(); - final ActivityInfo aInfoDefault = new ActivityInfo(); - aInfoDefault.name = "fakeHomeActivity"; - aInfoDefault.applicationInfo = new ApplicationInfo(); - aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; - doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(), - refEq(homeIntent)); + mockResolveHomeActivity(); final List<ResolveInfo> resolutions = new ArrayList<>(); final ResolveInfo infoFake1 = new ResolveInfo(); @@ -646,4 +645,19 @@ public class RootActivityContainerTests extends ActivityTestsBase { resolvedInfo.first.applicationInfo.packageName); assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name); } + + /** + * Mock {@link RootActivityContainerTests#resolveHomeActivity} for returning consistent activity + * info for test cases (the original implementation will resolve from the real package manager). + */ + private ActivityInfo mockResolveHomeActivity() { + final Intent homeIntent = mService.getHomeIntent(); + final ActivityInfo aInfoDefault = new ActivityInfo(); + aInfoDefault.name = "fakeHomeActivity"; + aInfoDefault.applicationInfo = new ApplicationInfo(); + aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; + doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(), + refEq(homeIntent)); + return aInfoDefault; + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 715353e5d980..3a8d3b74c08f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -476,4 +476,47 @@ public class WindowStateTests extends WindowTestsBase { root.prepareWindowToDisplayDuringRelayout(wasVisible /*wasVisible*/); verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString()); } + + @Test + public void testVisibilityChangeSwitchUser() { + final WindowState window = createWindow(null, TYPE_APPLICATION, "app"); + window.mHasSurface = true; + window.setShowToOwnerOnlyLocked(true); + + mWm.mCurrentUserId = 1; + window.switchUser(); + assertFalse(window.isVisible()); + assertFalse(window.isVisibleByPolicy()); + + mWm.mCurrentUserId = 0; + window.switchUser(); + assertTrue(window.isVisible()); + assertTrue(window.isVisibleByPolicy()); + } + + @Test + public void testGetTransformationMatrix() { + synchronized (mWm.mGlobalLock) { + final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0"); + win0.getFrameLw().offsetTo(1, 0); + + final DisplayContent dc = createNewDisplay(); + dc.reparentDisplayContent(win0, win0.getSurfaceControl()); + dc.updateLocation(win0, 2, 0); + + final float[] values = new float[9]; + final Matrix matrix = new Matrix(); + final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); + final WindowState win1 = createWindow(null, TYPE_APPLICATION, dc, "win1"); + win1.mHasSurface = true; + win1.mSurfaceControl = mock(SurfaceControl.class); + win1.getFrameLw().offsetTo(3, 0); + win1.updateSurfacePosition(t); + win1.getTransformationMatrix(values, matrix); + + matrix.getValues(values); + assertEquals(6f, values[Matrix.MTRANS_X], 0f); + assertEquals(0f, values[Matrix.MTRANS_Y], 0f); + } + } } diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java index 4b33e165bfeb..77866279a751 100644 --- a/services/usage/java/com/android/server/usage/AppStandbyController.java +++ b/services/usage/java/com/android/server/usage/AppStandbyController.java @@ -27,6 +27,7 @@ import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOU import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START; +import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN; @@ -238,6 +239,11 @@ public class AppStandbyController { long mUnexemptedSyncScheduledTimeoutMillis; /** Maximum time a system interaction should keep the buckets elevated. */ long mSystemInteractionTimeoutMillis; + /** + * Maximum time a foreground service start should keep the buckets elevated if the service + * start is the first usage of the app + */ + long mInitialForegroundServiceStartTimeoutMillis; /** The length of time phone must be charging before considered stable enough to run jobs */ long mStableChargingThresholdMillis; @@ -877,7 +883,8 @@ public class AppStandbyController { || event.mEventType == UsageEvents.Event.USER_INTERACTION || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN || event.mEventType == UsageEvents.Event.SLICE_PINNED - || event.mEventType == UsageEvents.Event.SLICE_PINNED_PRIV)) { + || event.mEventType == UsageEvents.Event.SLICE_PINNED_PRIV + || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START)) { final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory( event.mPackage, userId, elapsedRealtime); @@ -898,6 +905,13 @@ public class AppStandbyController { STANDBY_BUCKET_ACTIVE, subReason, 0, elapsedRealtime + mSystemInteractionTimeoutMillis); nextCheckTime = mSystemInteractionTimeoutMillis; + } else if (event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START) { + // Only elevate bucket if this is the first usage of the app + if (prevBucket != STANDBY_BUCKET_NEVER) return; + mAppIdleHistory.reportUsage(appHistory, event.mPackage, + STANDBY_BUCKET_ACTIVE, subReason, + 0, elapsedRealtime + mInitialForegroundServiceStartTimeoutMillis); + nextCheckTime = mInitialForegroundServiceStartTimeoutMillis; } else { mAppIdleHistory.reportUsage(appHistory, event.mPackage, STANDBY_BUCKET_ACTIVE, subReason, @@ -930,6 +944,8 @@ public class AppStandbyController { case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN; case UsageEvents.Event.SLICE_PINNED: return REASON_SUB_USAGE_SLICE_PINNED; case UsageEvents.Event.SLICE_PINNED_PRIV: return REASON_SUB_USAGE_SLICE_PINNED_PRIV; + case UsageEvents.Event.FOREGROUND_SERVICE_START: + return REASON_SUB_USAGE_FOREGROUND_SERVICE_START; default: return 0; } } @@ -1509,6 +1525,9 @@ public class AppStandbyController { pw.print(" mSystemInteractionTimeoutMillis="); TimeUtils.formatDuration(mSystemInteractionTimeoutMillis, pw); pw.println(); + pw.print(" mInitialForegroundServiceStartTimeoutMillis="); + TimeUtils.formatDuration(mInitialForegroundServiceStartTimeoutMillis, pw); + pw.println(); pw.print(" mPredictionTimeoutMillis="); TimeUtils.formatDuration(mPredictionTimeoutMillis, pw); @@ -1848,6 +1867,8 @@ public class AppStandbyController { "unexempted_sync_scheduled_duration"; private static final String KEY_SYSTEM_INTERACTION_HOLD_DURATION = "system_interaction_duration"; + private static final String KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION = + "initial_foreground_service_start_duration"; private static final String KEY_STABLE_CHARGING_THRESHOLD = "stable_charging_threshold"; public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR; public static final long DEFAULT_NOTIFICATION_TIMEOUT = 12 * ONE_HOUR; @@ -1859,6 +1880,7 @@ public class AppStandbyController { public static final long DEFAULT_EXEMPTED_SYNC_START_TIMEOUT = 10 * ONE_MINUTE; public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT = 10 * ONE_MINUTE; public static final long DEFAULT_STABLE_CHARGING_THRESHOLD = 10 * ONE_MINUTE; + public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = 30 * ONE_MINUTE; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -1964,6 +1986,12 @@ public class AppStandbyController { mSystemInteractionTimeoutMillis = mParser.getDurationMillis( KEY_SYSTEM_INTERACTION_HOLD_DURATION, COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYSTEM_INTERACTION_TIMEOUT); + + mInitialForegroundServiceStartTimeoutMillis = mParser.getDurationMillis( + KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION, + COMPRESS_TIME ? ONE_MINUTE : + DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT); + mStableChargingThresholdMillis = mParser.getDurationMillis( KEY_STABLE_CHARGING_THRESHOLD, COMPRESS_TIME ? ONE_MINUTE : DEFAULT_STABLE_CHARGING_THRESHOLD); diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index a814c03ff9ad..5cd46ca936a7 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -301,4 +301,9 @@ interface ITelecomService { void setTestAutoModeApp(String packageName); + /** + * @see TelecomServiceImpl#setTestDefaultDialer + */ + void setTestDefaultDialer(in String packageName); + } diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java index 785d7aea2a6f..9a8d7cd35cb0 100644 --- a/telephony/java/android/telephony/UiccSlotInfo.java +++ b/telephony/java/android/telephony/UiccSlotInfo.java @@ -140,6 +140,10 @@ public class UiccSlotInfo implements Parcelable { return mIsEuicc; } + /** + * Returns the ICCID of a the UICC in the given slot, or the EID if it is an eUICC. Note that if + * the value is unavailble this will return null. + */ public String getCardId() { return mCardId; } diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp index e556b0acb1a3..1932871d86b9 100644 --- a/tests/RollbackTest/Android.bp +++ b/tests/RollbackTest/Android.bp @@ -29,6 +29,14 @@ android_test_helper_app { } android_test_helper_app { + name: "RollbackTestAppAv3", + manifest: "TestApp/Av3.xml", + sdk_version: "current", + srcs: ["TestApp/src/**/*.java"], + resource_dirs: ["TestApp/res_v3"], +} + +android_test_helper_app { name: "RollbackTestAppACrashingV2", manifest: "TestApp/ACrashingV2.xml", sdk_version: "current", @@ -118,6 +126,7 @@ android_test { java_resources: [ ":RollbackTestAppAv1", ":RollbackTestAppAv2", + ":RollbackTestAppAv3", ":RollbackTestAppACrashingV2", ":RollbackTestAppBv1", ":RollbackTestAppBv2", diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java index f9304f242ef3..13b5b9ac104e 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java @@ -775,6 +775,42 @@ public class RollbackTest { } } + /** + * Test failure to enable rollback for multi-package installs. + * If any one of the packages fail to enable rollback, we shouldn't enable + * rollback for any package. + */ + @Test + public void testMultiPackageEnableFail() throws Exception { + try { + RollbackTestUtils.adoptShellPermissionIdentity( + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.DELETE_PACKAGES, + Manifest.permission.TEST_MANAGE_ROLLBACKS); + RollbackManager rm = RollbackTestUtils.getRollbackManager(); + + RollbackTestUtils.uninstall(TEST_APP_A); + RollbackTestUtils.uninstall(TEST_APP_B); + RollbackTestUtils.install("RollbackTestAppAv1.apk", false); + + // We should fail to enable rollback here because TestApp B is not + // already installed. + RollbackTestUtils.installMultiPackage(true, + "RollbackTestAppAv2.apk", + "RollbackTestAppBv2.apk"); + + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); + + assertNull(getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A)); + assertNull(getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_B)); + } finally { + RollbackTestUtils.dropShellPermissionIdentity(); + } + } + @Test @Ignore("b/120200473") /** @@ -871,6 +907,51 @@ public class RollbackTest { } } + /** + * Test race between roll back and roll forward. + */ + @Test + public void testRollForwardRace() throws Exception { + try { + RollbackTestUtils.adoptShellPermissionIdentity( + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.DELETE_PACKAGES, + Manifest.permission.TEST_MANAGE_ROLLBACKS, + Manifest.permission.MANAGE_ROLLBACKS); + + RollbackManager rm = RollbackTestUtils.getRollbackManager(); + + RollbackTestUtils.uninstall(TEST_APP_A); + RollbackTestUtils.install("RollbackTestAppAv1.apk", false); + RollbackTestUtils.install("RollbackTestAppAv2.apk", true); + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + + RollbackInfo rollback = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A); + assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); + + // Install a new version of package A, then immediately rollback + // the previous version. We expect the rollback to fail, because + // it is no longer available. + // There are a couple different ways this could fail depending on + // thread interleaving, so don't ignore flaky failures. + RollbackTestUtils.install("RollbackTestAppAv3.apk", false); + try { + RollbackTestUtils.rollback(rollback.getRollbackId()); + // Note: Don't ignore flaky failures here. + fail("Expected rollback to fail, but it did not."); + } catch (AssertionError e) { + Log.i(TAG, "Note expected failure: ", e); + // Expected + } + + // Note: Don't ignore flaky failures here. + assertEquals(3, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + } finally { + RollbackTestUtils.dropShellPermissionIdentity(); + } + } + // Helper function to test that the given rollback info is a rollback for // the atomic set {A2, B2} -> {A1, B1}. private void assertRollbackInfoForAandB(RollbackInfo rollback) { diff --git a/tests/RollbackTest/TestApp/Av3.xml b/tests/RollbackTest/TestApp/Av3.xml new file mode 100644 index 000000000000..9725c9f7cf9e --- /dev/null +++ b/tests/RollbackTest/TestApp/Av3.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.tests.rollback.testapp.A" + android:versionCode="3" + android:versionName="3.0" > + + + <uses-sdk android:minSdkVersion="19" /> + + <application android:label="Rollback Test App A v3"> + <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData" + android:exported="true" /> + <activity android:name="com.android.tests.rollback.testapp.MainActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/tests/RollbackTest/TestApp/res_v3/values-anydpi/values.xml b/tests/RollbackTest/TestApp/res_v3/values-anydpi/values.xml new file mode 100644 index 000000000000..f2d8992bee37 --- /dev/null +++ b/tests/RollbackTest/TestApp/res_v3/values-anydpi/values.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> + +<resources> + <integer name="split_version">3</integer> +</resources> diff --git a/tests/RollbackTest/TestApp/res_v3/values/values.xml b/tests/RollbackTest/TestApp/res_v3/values/values.xml new file mode 100644 index 000000000000..968168a4bf06 --- /dev/null +++ b/tests/RollbackTest/TestApp/res_v3/values/values.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> + +<resources> + <integer name="app_version">3</integer> + <integer name="split_version">0</integer> +</resources> diff --git a/tools/bit/main.cpp b/tools/bit/main.cpp index 1a91f52bc6cf..d80c2e742fae 100644 --- a/tools/bit/main.cpp +++ b/tools/bit/main.cpp @@ -290,8 +290,14 @@ TestResults::OnTestStatus(TestStatus& status) m_currentAction->target->name.c_str(), className.c_str(), testName.c_str(), g_escapeEndColor); - string stack = get_bundle_string(results, &found, "stack", NULL); - if (found) { + bool stackFound; + string stack = get_bundle_string(results, &stackFound, "stack", NULL); + if (status.has_logcat()) { + const string logcat = status.logcat(); + if (logcat.length() > 0) { + printf("%s\n", logcat.c_str()); + } + } else if (stackFound) { printf("%s\n", stack.c_str()); } } |