Merge "Write staged sessions to /data/staging."
diff --git a/Android.bp b/Android.bp
index 391bcd5..cd0720d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -634,6 +634,7 @@
"wifi/java/android/net/wifi/rtt/IRttCallback.aidl",
"wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl",
"wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl",
+ "wifi/java/android/net/wifi/IDppCallback.aidl",
"wifi/java/android/net/wifi/IWifiScanner.aidl",
"packages/services/PacProcessor/com/android/net/IProxyService.aidl",
"packages/services/Proxy/com/android/net/IProxyCallback.aidl",
diff --git a/apct-tests/perftests/autofill/Android.mk b/apct-tests/perftests/autofill/Android.mk
index 28555a0..f4da40b 100644
--- a/apct-tests/perftests/autofill/Android.mk
+++ b/apct-tests/perftests/autofill/Android.mk
@@ -22,7 +22,7 @@
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
+ androidx.test.rules \
androidx.annotation_annotation \
apct-perftests-utils
diff --git a/apct-tests/perftests/autofill/AndroidManifest.xml b/apct-tests/perftests/autofill/AndroidManifest.xml
index e706a32..9c8abc3 100644
--- a/apct-tests/perftests/autofill/AndroidManifest.xml
+++ b/apct-tests/perftests/autofill/AndroidManifest.xml
@@ -35,6 +35,6 @@
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.perftests.autofill"/>
</manifest>
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
index 86a5c10..6979f0f 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
@@ -16,34 +16,23 @@
package android.view.autofill;
+import static org.junit.Assert.assertTrue;
+
import android.os.Looper;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.SettingsHelper;
import android.perftests.utils.SettingsStateKeeperRule;
-import android.perftests.utils.ShellHelper;
-import android.view.View;
import android.perftests.utils.StubActivity;
import android.provider.Settings;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.InstrumentationRegistry;
-import com.android.perftests.autofill.R;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
-import java.util.Locale;
-import java.util.Collection;
-import java.util.Arrays;
-
-import org.junit.Test;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.assertTrue;
/**
* Base class for all autofill tests.
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
index 62662e4..8090826 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
@@ -16,34 +16,18 @@
package android.view.autofill;
-import android.app.Activity;
-import android.os.Looper;
-import android.os.Bundle;
-import android.perftests.utils.PerfStatusReporter;
-import android.util.Log;
-import android.view.View;
-import android.widget.EditText;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.StubActivity;
-import android.provider.Settings;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.InstrumentationRegistry;
-import com.android.perftests.autofill.R;
-
-import java.util.Locale;
-import java.util.Collection;
-import java.util.Arrays;
-
-import org.junit.Test;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.runner.RunWith;
-
import static android.view.autofill.AutofillManager.AutofillCallback.EVENT_INPUT_HIDDEN;
import static android.view.autofill.AutofillManager.AutofillCallback.EVENT_INPUT_SHOWN;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.StubActivity;
+import android.view.View;
+import android.widget.EditText;
+
+import com.android.perftests.autofill.R;
+
+import org.junit.Test;
+
public class LoginTest extends AbstractAutofillPerfTestCase {
private EditText mUsername;
diff --git a/apct-tests/perftests/utils/Android.mk b/apct-tests/perftests/utils/Android.mk
index 604f0ad..19f83c8 100644
--- a/apct-tests/perftests/utils/Android.mk
+++ b/apct-tests/perftests/utils/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
+ androidx.test.rules \
androidx.annotation_annotation
# Build all java files in the java subdirectory
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
index da17818..93bf541 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
@@ -20,9 +20,10 @@
import android.app.Instrumentation;
import android.os.Bundle;
import android.os.Debug;
-import android.support.test.InstrumentationRegistry;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+
import java.io.File;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java b/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java
index 0de6f1d..8187c6f 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java
@@ -16,7 +16,7 @@
package android.perftests.utils;
-import android.support.test.InstrumentationRegistry;
+import androidx.test.InstrumentationRegistry;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/PerfStatusReporter.java b/apct-tests/perftests/utils/src/android/perftests/utils/PerfStatusReporter.java
index 4b7b98d..d54a851 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/PerfStatusReporter.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/PerfStatusReporter.java
@@ -16,16 +16,17 @@
package android.perftests.utils;
-import android.support.test.InstrumentationRegistry;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
/**
* Use this rule to make sure we report the status after the test success.
*
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java b/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java
index 895547d..7b52576 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java
@@ -17,14 +17,13 @@
import android.app.UiAutomation;
import android.os.ParcelFileDescriptor;
-import android.support.test.InstrumentationRegistry;
import android.text.TextUtils;
import android.util.AndroidRuntimeException;
-import android.util.Log;
-
-import java.io.FileInputStream;
import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+
+import java.io.FileInputStream;
/**
* Provides Shell-based utilities such as running a command.
diff --git a/api/current.txt b/api/current.txt
index c107e58..fad607c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -29555,6 +29555,7 @@
method public int getWifiState();
method public boolean is5GHzBandSupported();
method public boolean isDeviceToApRttSupported();
+ method public boolean isDppSupported();
method public boolean isEnhancedPowerReportingSupported();
method public boolean isOweSupported();
method public boolean isP2pSupported();
diff --git a/api/system-current.txt b/api/system-current.txt
index 7f3c152..01304a6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -94,6 +94,7 @@
field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final java.lang.String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
field public static final java.lang.String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
+ field public static final java.lang.String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
field public static final java.lang.String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
field public static final java.lang.String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
field public static final java.lang.String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
@@ -1038,6 +1039,7 @@
field public static final java.lang.String OEM_LOCK_SERVICE = "oem_lock";
field public static final java.lang.String PERMISSION_SERVICE = "permission";
field public static final java.lang.String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
+ field public static final java.lang.String ROLLBACK_SERVICE = "rollback";
field public static final java.lang.String SECURE_ELEMENT_SERVICE = "secure_element";
field public static final java.lang.String STATS_MANAGER = "stats";
field public static final java.lang.String SYSTEM_UPDATE_SERVICE = "system_update";
@@ -1069,6 +1071,7 @@
field public static final java.lang.String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS";
field public static final java.lang.String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS";
field public static final java.lang.String ACTION_MASTER_CLEAR_NOTIFICATION = "android.intent.action.MASTER_CLEAR_NOTIFICATION";
+ field public static final java.lang.String ACTION_PACKAGE_ROLLBACK_EXECUTED = "android.intent.action.PACKAGE_ROLLBACK_EXECUTED";
field public static final java.lang.String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
@@ -1204,6 +1207,7 @@
method public void setAllocateAggressive(boolean);
method public void setAllowDowngrade(boolean);
method public void setDontKillApp(boolean);
+ method public void setEnableRollback();
method public void setGrantedRuntimePermissions(java.lang.String[]);
method public void setInstallAsInstantApp(boolean);
method public void setInstallAsVirtualPreload();
@@ -1221,7 +1225,6 @@
public abstract class PackageManager {
method public abstract void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
method public abstract boolean arePermissionsIndividuallyControlled();
- method public boolean canSuspendPackage(java.lang.String);
method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
method public android.content.pm.ApplicationInfo getApplicationInfoAsUser(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.dex.ArtManager getArtManager();
@@ -1235,6 +1238,7 @@
method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
method public abstract int getIntentVerificationStatusAsUser(java.lang.String, int);
method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
+ method public java.lang.String[] getUnsuspendablePackages(java.lang.String[]);
method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract int installExistingPackage(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -1398,6 +1402,41 @@
}
+package android.content.rollback {
+
+ public final class PackageRollbackInfo implements android.os.Parcelable {
+ ctor public PackageRollbackInfo(java.lang.String, android.content.rollback.PackageRollbackInfo.PackageVersion, android.content.rollback.PackageRollbackInfo.PackageVersion);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.content.rollback.PackageRollbackInfo> CREATOR;
+ field public final android.content.rollback.PackageRollbackInfo.PackageVersion higherVersion;
+ field public final android.content.rollback.PackageRollbackInfo.PackageVersion lowerVersion;
+ field public final java.lang.String packageName;
+ }
+
+ public static class PackageRollbackInfo.PackageVersion {
+ ctor public PackageRollbackInfo.PackageVersion(long);
+ field public final long versionCode;
+ }
+
+ public final class RollbackInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.content.rollback.RollbackInfo> CREATOR;
+ field public final android.content.rollback.PackageRollbackInfo targetPackage;
+ }
+
+ public final class RollbackManager {
+ method public void executeRollback(android.content.rollback.RollbackInfo, android.content.IntentSender);
+ method public void expireRollbackForPackage(java.lang.String);
+ method public android.content.rollback.RollbackInfo getAvailableRollback(java.lang.String);
+ method public java.util.List<java.lang.String> getPackagesWithAvailableRollbacks();
+ method public java.util.List<android.content.rollback.RollbackInfo> getRecentlyExecutedRollbacks();
+ method public void reloadPersistedData();
+ }
+
+}
+
package android.hardware {
public final class Sensor {
@@ -3001,6 +3040,7 @@
field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
field public static final int FLAG_FROM_KEY = 65536; // 0x10000
+ field public static final int SUCCESS = 0; // 0x0
}
public static abstract class AudioManager.AudioServerStateCallback {
@@ -3117,8 +3157,10 @@
method public int detachMixes(java.util.List<android.media.audiopolicy.AudioMix>);
method public int getFocusDuckingBehavior();
method public int getStatus();
+ method public int removeUidDeviceAffinity(int);
method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setRegistration(java.lang.String);
+ method public int setUidDeviceAffinity(int, java.util.List<android.media.AudioDeviceInfo>);
method public java.lang.String toLogFriendlyString();
field public static final int FOCUS_POLICY_DUCKING_DEFAULT = 0; // 0x0
field public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; // 0x0
@@ -3160,34 +3202,6 @@
package android.media.session {
- public final class ControllerCallbackLink implements android.os.Parcelable {
- ctor public ControllerCallbackLink(android.media.session.ControllerCallbackLink.CallbackStub);
- method public int describeContents();
- method public android.os.IBinder getBinder();
- method public void notifyEvent(java.lang.String, android.os.Bundle);
- method public void notifyExtrasChanged(android.os.Bundle);
- method public void notifyMetadataChanged(android.media.MediaMetadata);
- method public void notifyPlaybackStateChanged(android.media.session.PlaybackState);
- method public void notifyQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
- method public void notifyQueueTitleChanged(java.lang.CharSequence);
- method public void notifySessionDestroyed();
- method public void notifyVolumeInfoChanged(int, android.media.AudioAttributes, int, int, int);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.media.session.ControllerCallbackLink> CREATOR;
- }
-
- public static abstract class ControllerCallbackLink.CallbackStub {
- ctor public ControllerCallbackLink.CallbackStub();
- method public void onEvent(java.lang.String, android.os.Bundle);
- method public void onExtrasChanged(android.os.Bundle);
- method public void onMetadataChanged(android.media.MediaMetadata);
- method public void onPlaybackStateChanged(android.media.session.PlaybackState);
- method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
- method public void onQueueTitleChanged(java.lang.CharSequence);
- method public void onSessionDestroyed();
- method public void onVolumeInfoChanged(int, android.media.AudioAttributes, int, int, int);
- }
-
public final class MediaSessionManager {
method public void setOnMediaKeyListener(android.media.session.MediaSessionManager.OnMediaKeyListener, android.os.Handler);
method public void setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, android.os.Handler);
@@ -3201,64 +3215,6 @@
method public abstract void onVolumeKeyLongPress(android.view.KeyEvent);
}
- public final class SessionCallbackLink implements android.os.Parcelable {
- ctor public SessionCallbackLink(android.media.session.SessionCallbackLink.CallbackStub);
- method public int describeContents();
- method public android.os.IBinder getBinder();
- method public void notifyAdjustVolume(java.lang.String, int, int, android.media.session.ControllerCallbackLink, int);
- method public void notifyCommand(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle, android.os.ResultReceiver);
- method public void notifyCustomAction(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
- method public void notifyFastForward(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
- method public void notifyMediaButton(java.lang.String, int, int, android.content.Intent, int, android.os.ResultReceiver);
- method public void notifyMediaButtonFromController(java.lang.String, int, int, android.media.session.ControllerCallbackLink, android.content.Intent);
- method public void notifyNext(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
- method public void notifyPause(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
- method public void notifyPlay(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
- method public void notifyPlayFromMediaId(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
- method public void notifyPlayFromSearch(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
- method public void notifyPlayFromUri(java.lang.String, int, int, android.media.session.ControllerCallbackLink, android.net.Uri, android.os.Bundle);
- method public void notifyPrepare(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
- method public void notifyPrepareFromMediaId(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
- method public void notifyPrepareFromSearch(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
- method public void notifyPrepareFromUri(java.lang.String, int, int, android.media.session.ControllerCallbackLink, android.net.Uri, android.os.Bundle);
- method public void notifyPrevious(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
- method public void notifyRate(java.lang.String, int, int, android.media.session.ControllerCallbackLink, android.media.Rating);
- method public void notifyRewind(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
- method public void notifySeekTo(java.lang.String, int, int, android.media.session.ControllerCallbackLink, long);
- method public void notifySetVolumeTo(java.lang.String, int, int, android.media.session.ControllerCallbackLink, int);
- method public void notifySkipToTrack(java.lang.String, int, int, android.media.session.ControllerCallbackLink, long);
- method public void notifyStop(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.media.session.SessionCallbackLink> CREATOR;
- }
-
- public static abstract class SessionCallbackLink.CallbackStub {
- ctor public SessionCallbackLink.CallbackStub();
- method public void onAdjustVolume(java.lang.String, int, int, android.media.session.ControllerCallbackLink, int);
- method public void onCommand(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle, android.os.ResultReceiver);
- method public void onCustomAction(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
- method public void onFastForward(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
- method public void onMediaButton(java.lang.String, int, int, android.content.Intent, int, android.os.ResultReceiver);
- method public void onMediaButtonFromController(java.lang.String, int, int, android.media.session.ControllerCallbackLink, android.content.Intent);
- method public void onNext(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
- method public void onPause(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
- method public void onPlay(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
- method public void onPlayFromMediaId(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
- method public void onPlayFromSearch(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
- method public void onPlayFromUri(java.lang.String, int, int, android.media.session.ControllerCallbackLink, android.net.Uri, android.os.Bundle);
- method public void onPrepare(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
- method public void onPrepareFromMediaId(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
- method public void onPrepareFromSearch(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
- method public void onPrepareFromUri(java.lang.String, int, int, android.media.session.ControllerCallbackLink, android.net.Uri, android.os.Bundle);
- method public void onPrevious(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
- method public void onRate(java.lang.String, int, int, android.media.session.ControllerCallbackLink, android.media.Rating);
- method public void onRewind(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
- method public void onSeekTo(java.lang.String, int, int, android.media.session.ControllerCallbackLink, long);
- method public void onSetVolumeTo(java.lang.String, int, int, android.media.session.ControllerCallbackLink, int);
- method public void onSkipToTrack(java.lang.String, int, int, android.media.session.ControllerCallbackLink, long);
- method public void onStop(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
- }
-
}
package android.media.soundtrigger {
@@ -3715,6 +3671,26 @@
package android.net.wifi {
+ public abstract class DppStatusCallback {
+ ctor public DppStatusCallback();
+ method public abstract void onConfiguratorSuccess(int);
+ method public abstract void onEnrolleeSuccess(int);
+ method public abstract void onFailure(int);
+ method public abstract void onProgress(int);
+ field public static final int DPP_EVENT_FAILURE = -7; // 0xfffffff9
+ field public static final int DPP_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe
+ field public static final int DPP_EVENT_FAILURE_BUSY = -5; // 0xfffffffb
+ field public static final int DPP_EVENT_FAILURE_CONFIGURATION = -4; // 0xfffffffc
+ field public static final int DPP_EVENT_FAILURE_INVALID_NETWORK = -9; // 0xfffffff7
+ field public static final int DPP_EVENT_FAILURE_INVALID_URI = -1; // 0xffffffff
+ field public static final int DPP_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd
+ field public static final int DPP_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8
+ field public static final int DPP_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa
+ field public static final int DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; // 0x0
+ field public static final int DPP_EVENT_PROGRESS_RESPONSE_PENDING = 1; // 0x1
+ field public static final int DPP_EVENT_SUCCESS_CONFIGURATION_SENT = 0; // 0x0
+ }
+
public deprecated class RttManager {
method public void disableResponder(android.net.wifi.RttManager.ResponderCallback);
method public void enableResponder(android.net.wifi.RttManager.ResponderCallback);
@@ -3944,7 +3920,10 @@
method public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
method public void setDeviceMobilityState(int);
method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
+ method public void startDppAsConfiguratorInitiator(java.lang.String, int, int, android.os.Handler, android.net.wifi.DppStatusCallback);
+ method public void startDppAsEnrolleeInitiator(java.lang.String, android.os.Handler, android.net.wifi.DppStatusCallback);
method public boolean startScan(android.os.WorkSource);
+ method public void stopDppSession();
method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback);
field public static final int CHANGE_REASON_ADDED = 0; // 0x0
field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2
@@ -3954,6 +3933,8 @@
field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2
field public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; // 0x3
field public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; // 0x0
+ field public static final int DPP_NETWORK_ROLE_AP = 1; // 0x1
+ field public static final int DPP_NETWORK_ROLE_STA = 0; // 0x0
field public static final java.lang.String EXTRA_CHANGE_REASON = "changeReason";
field public static final java.lang.String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
field public static final java.lang.String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
@@ -7955,6 +7936,7 @@
method public void callDrawGlFunction(android.graphics.Canvas, long, java.lang.Runnable);
method public boolean canInvokeDrawGlFunctor(android.view.View);
method public void detachDrawGlFunctor(android.view.View, long);
+ method public void drawWebViewFunctor(android.graphics.Canvas, int);
method public android.app.Application getApplication();
method public java.lang.String getDataDirectorySuffix();
method public java.lang.String getErrorString(android.content.Context, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index c1e4162..1401cbb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -72,6 +72,7 @@
method public void resizeStack(int, android.graphics.Rect) throws java.lang.SecurityException;
method public void resizeStack(int, android.graphics.Rect, boolean);
method public void resizeTask(int, android.graphics.Rect);
+ method public void setDisplayToSingleTaskInstance(int);
method public void setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
method public void setTaskWindowingModeSplitScreenPrimary(int, int, boolean, boolean, android.graphics.Rect, boolean) throws java.lang.SecurityException;
method public void startSystemLockTaskMode(int);
@@ -329,11 +330,6 @@
method public android.os.UserHandle getUser();
method public int getUserId();
method public void setAutofillCompatibilityEnabled(boolean);
- field public static final java.lang.String ROLLBACK_SERVICE = "rollback";
- }
-
- public class Intent implements java.lang.Cloneable android.os.Parcelable {
- field public static final java.lang.String ACTION_PACKAGE_ROLLBACK_EXECUTED = "android.intent.action.PACKAGE_ROLLBACK_EXECUTED";
}
}
@@ -356,10 +352,6 @@
ctor public LauncherApps(android.content.Context);
}
- public static class PackageInstaller.SessionParams implements android.os.Parcelable {
- method public void setEnableRollback();
- }
-
public abstract class PackageManager {
method public abstract boolean arePermissionsIndividuallyControlled();
method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
@@ -407,42 +399,6 @@
}
-package android.content.rollback {
-
- public final class PackageRollbackInfo implements android.os.Parcelable {
- ctor public PackageRollbackInfo(java.lang.String, android.content.rollback.PackageRollbackInfo.PackageVersion, android.content.rollback.PackageRollbackInfo.PackageVersion);
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.content.rollback.PackageRollbackInfo> CREATOR;
- field public final android.content.rollback.PackageRollbackInfo.PackageVersion higherVersion;
- field public final android.content.rollback.PackageRollbackInfo.PackageVersion lowerVersion;
- field public final java.lang.String packageName;
- }
-
- public static class PackageRollbackInfo.PackageVersion {
- ctor public PackageRollbackInfo.PackageVersion(long);
- field public final long versionCode;
- }
-
- public final class RollbackInfo implements android.os.Parcelable {
- ctor public RollbackInfo(android.content.rollback.PackageRollbackInfo);
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.content.rollback.RollbackInfo> CREATOR;
- field public final android.content.rollback.PackageRollbackInfo targetPackage;
- }
-
- public final class RollbackManager {
- method public void executeRollback(android.content.rollback.RollbackInfo, android.content.IntentSender);
- method public void expireRollbackForPackage(java.lang.String);
- method public android.content.rollback.RollbackInfo getAvailableRollback(java.lang.String);
- method public java.util.List<java.lang.String> getPackagesWithAvailableRollbacks();
- method public java.util.List<android.content.rollback.RollbackInfo> getRecentlyExecutedRollbacks();
- method public void reloadPersistedData();
- }
-
-}
-
package android.database.sqlite {
public class SQLiteCompatibilityWalFlags {
@@ -1153,6 +1109,20 @@
method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
}
+ public final class CompositeUserData implements android.os.Parcelable {
+ ctor public CompositeUserData(android.service.autofill.UserData, android.service.autofill.UserData);
+ method public int describeContents();
+ method public java.lang.String[] getCategoryIds();
+ method public android.os.Bundle getDefaultFieldClassificationArgs();
+ method public java.lang.String getFieldClassificationAlgorithm();
+ method public java.lang.String getFieldClassificationAlgorithmForCategory(java.lang.String);
+ method public android.util.ArrayMap<java.lang.String, java.lang.String> getFieldClassificationAlgorithms();
+ method public android.util.ArrayMap<java.lang.String, android.os.Bundle> getFieldClassificationArgs();
+ method public java.lang.String[] getValues();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.CompositeUserData> CREATOR;
+ }
+
public final class CustomDescription implements android.os.Parcelable {
method public android.util.SparseArray<android.service.autofill.InternalOnClickAction> getActions();
}
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index fe7099b..abe18ba 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -23,6 +23,7 @@
],
tidy_flags: [
"-system-headers",
+ "-warnings-as-errors=*",
],
}
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index ded20c4..c6d2bc8 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -19,12 +19,12 @@
import android.app.ActivityManager;
import android.content.Context;
-import android.media.AudioAttributes;
+import android.content.pm.ParceledListSlice;
import android.media.MediaMetadata;
-import android.media.session.ControllerCallbackLink;
import android.media.session.ISessionController;
+import android.media.session.ISessionControllerCallback;
import android.media.session.ISessionManager;
-import android.media.session.MediaSession.QueueItem;
+import android.media.session.ParcelableVolumeInfo;
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.HandlerThread;
@@ -178,7 +178,13 @@
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
}
- class ControllerCallbackStub extends ControllerCallbackLink.CallbackStub {
+ class ControllerMonitor extends ISessionControllerCallback.Stub {
+ private final ISessionController mController;
+
+ public ControllerMonitor(ISessionController controller) {
+ mController = controller;
+ }
+
@Override
public void onSessionDestroyed() {
System.out.println("onSessionDestroyed. Enter q to quit.");
@@ -202,37 +208,24 @@
}
@Override
- public void onQueueChanged(List<QueueItem> queue) {
+ public void onQueueChanged(ParceledListSlice queue) throws RemoteException {
System.out.println("onQueueChanged, "
- + (queue == null ? "null queue" : " size=" + queue.size()));
+ + (queue == null ? "null queue" : " size=" + queue.getList().size()));
}
@Override
- public void onQueueTitleChanged(CharSequence title) {
+ public void onQueueTitleChanged(CharSequence title) throws RemoteException {
System.out.println("onQueueTitleChange " + title);
}
@Override
- public void onExtrasChanged(Bundle extras) {
+ public void onExtrasChanged(Bundle extras) throws RemoteException {
System.out.println("onExtrasChanged " + extras);
}
@Override
- public void onVolumeInfoChanged(int volumeType, AudioAttributes attrs, int controlType,
- int maxVolume, int currentVolume) {
- System.out.println("onVolumeInfoChanged " + "volumeType=" + volumeType + ", attrs="
- + attrs + ", controlType=" + controlType + ", maxVolume=" + maxVolume
- + ", currentVolume=" + currentVolume);
- }
- }
-
- private class ControllerMonitor {
- private final ISessionController mController;
- private final ControllerCallbackLink mControllerCallbackLink;
-
- ControllerMonitor(ISessionController controller) {
- mController = controller;
- mControllerCallbackLink = new ControllerCallbackLink(new ControllerCallbackStub());
+ public void onVolumeInfoChanged(ParcelableVolumeInfo info) throws RemoteException {
+ System.out.println("onVolumeInfoChanged " + info);
}
void printUsageMessage() {
@@ -251,7 +244,7 @@
@Override
protected void onLooperPrepared() {
try {
- mController.registerCallbackListener(PACKAGE_NAME, mControllerCallbackLink);
+ mController.registerCallbackListener(PACKAGE_NAME, ControllerMonitor.this);
} catch (RemoteException e) {
System.out.println("Error registering monitor callback");
}
@@ -294,7 +287,7 @@
} finally {
cbThread.getLooper().quit();
try {
- mController.unregisterCallbackListener(mControllerCallbackLink);
+ mController.unregisterCallbackListener(this);
} catch (Exception e) {
// ignoring
}
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index f0b751d..d610f66 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -136,7 +136,7 @@
"android.hardware.power@1.0",
"android.hardware.power@1.1",
"android.hardware.power.stats@1.0",
- "android.hardware.thermal@1.0",
+ "android.hardware.thermal@2.0",
"libpackagelistparser",
"libsysutils",
"libcutils",
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index e541543..5c53a3a 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -2789,13 +2789,14 @@
* frameworks/base/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
*/
message Temperature {
- // The type of temperature being reported. Eg. CPU, GPU, SKIN, BATTERY.
+ // The type of temperature being reported. Eg. CPU, GPU, SKIN, BATTERY, BCL_.
optional android.os.TemperatureTypeEnum sensor_location = 1;
// The name of the temperature source. Eg. CPU0
optional string sensor_name = 2;
// Temperature in tenths of a degree C.
+ // For BCL, it is decimillivolt, decimilliamps, and percentage * 10.
optional int32 temperature_deci_celsius = 3;
}
diff --git a/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp b/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
index 33a17de..53709f1 100644
--- a/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
+++ b/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
@@ -17,7 +17,7 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
-#include <android/hardware/thermal/1.0/IThermal.h>
+#include <android/hardware/thermal/2.0/IThermal.h>
#include "external/ResourceThermalManagerPuller.h"
#include "external/StatsPuller.h"
@@ -31,10 +31,11 @@
using android::hardware::hidl_death_recipient;
using android::hardware::hidl_vec;
using android::hidl::base::V1_0::IBase;
-using android::hardware::thermal::V1_0::IThermal;
-using android::hardware::thermal::V1_0::Temperature;
-using android::hardware::thermal::V1_0::ThermalStatus;
-using android::hardware::thermal::V1_0::ThermalStatusCode;
+using ::android::hardware::thermal::V2_0::IThermal;
+using ::android::hardware::thermal::V2_0::Temperature;
+using ::android::hardware::thermal::V2_0::TemperatureType;
+using ::android::hardware::thermal::V1_0::ThermalStatus;
+using ::android::hardware::thermal::V1_0::ThermalStatusCode;
using android::hardware::Return;
using android::hardware::Void;
@@ -49,7 +50,7 @@
namespace statsd {
bool getThermalHalLocked();
-sp<android::hardware::thermal::V1_0::IThermal> gThermalHal = nullptr;
+sp<android::hardware::thermal::V2_0::IThermal> gThermalHal = nullptr;
std::mutex gThermalHalMutex;
struct ThermalHalDeathRecipient : virtual public hidl_death_recipient {
@@ -107,7 +108,7 @@
data->clear();
bool resultSuccess = true;
- Return<void> ret = gThermalHal->getTemperatures(
+ Return<void> ret = gThermalHal->getCurrentTemperatures(false, TemperatureType::SKIN,
[&](ThermalStatus status, const hidl_vec<Temperature>& temps) {
if (status.code != ThermalStatusCode::SUCCESS) {
ALOGE("Failed to get temperatures from ThermalHAL. Status: %d", status.code);
@@ -121,7 +122,7 @@
ptr->write((static_cast<int>(temps[i].type)));
ptr->write(temps[i].name);
// Convert the temperature to an int.
- int32_t temp = static_cast<int>(temps[i].currentValue * 10);
+ int32_t temp = static_cast<int>(temps[i].value * 10);
ptr->write(temp);
ptr->init();
data->push_back(ptr);
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 997ed25..e86fa89 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -19,6 +19,7 @@
import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -794,7 +795,7 @@
*
* @see android.R.styleable#AccessibilityService_nonInteractiveUiTimeout
*/
- public void setNonInteractiveUiTimeoutMillis(int timeout) {
+ public void setNonInteractiveUiTimeoutMillis(@IntRange(from = 0) int timeout) {
mNonInteractiveUiTimeout = timeout;
}
@@ -821,7 +822,7 @@
*
* @see android.R.styleable#AccessibilityService_interactiveUiTimeout
*/
- public void setInteractiveUiTimeoutMillis(int timeout) {
+ public void setInteractiveUiTimeoutMillis(@IntRange(from = 0) int timeout) {
mInteractiveUiTimeout = timeout;
}
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 4d8c856..7eab5db 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -430,4 +430,17 @@
e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Makes the display with the given id a single task instance display. I.e the display can only
+ * contain one task.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+ public void setDisplayToSingleTaskInstance(int displayId) {
+ try {
+ getService().setDisplayToSingleTaskInstance(displayId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 680fed8..0b5776e 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -83,6 +83,9 @@
private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
private Surface mTmpSurface = new Surface();
+ /** The ActivityView is only allowed to contain one task. */
+ private final boolean mSingleTaskInstance;
+
@UnsupportedAppUsage
public ActivityView(Context context) {
this(context, null /* attrs */);
@@ -93,7 +96,13 @@
}
public ActivityView(Context context, AttributeSet attrs, int defStyle) {
+ this(context, attrs, defStyle, false /*singleTaskInstance*/);
+ }
+
+ public ActivityView(
+ Context context, AttributeSet attrs, int defStyle, boolean singleTaskInstance) {
super(context, attrs, defStyle);
+ mSingleTaskInstance = singleTaskInstance;
mActivityTaskManager = ActivityTaskManager.getService();
mSurfaceView = new SurfaceView(context);
@@ -379,6 +388,9 @@
try {
wm.reparentDisplayContent(displayId, mRootSurfaceControl.getHandle());
wm.dontOverrideDisplayInfo(displayId);
+ if (mSingleTaskInstance) {
+ mActivityTaskManager.setDisplayToSingleTaskInstance(displayId);
+ }
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 17529a6..94983e1 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2299,9 +2299,9 @@
}
@Override
- public boolean canSuspendPackage(String packageName) {
+ public String[] getUnsuspendablePackages(String[] packageNames) {
try {
- return mPM.canSuspendPackageForUser(packageName, mContext.getUserId());
+ return mPM.getUnsuspendablePackagesForUser(packageNames, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index f549e18..dd87dc3 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -452,4 +452,10 @@
* Clears launch params for given packages.
*/
void clearLaunchParamsForPackages(in List<String> packageNames);
+
+ /**
+ * Makes the display with the given id a single task instance display. I.e the display can only
+ * contain one task.
+ */
+ void setDisplayToSingleTaskInstance(int displayId);
}
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index bf3d885..bbe5b8b 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -69,6 +69,22 @@
| DISABLE_SYSTEM_INFO | DISABLE_RECENT | DISABLE_HOME | DISABLE_BACK | DISABLE_CLOCK
| DISABLE_SEARCH;
+ @IntDef(flag = true, prefix = {"DISABLE_"}, value = {
+ DISABLE_NONE,
+ DISABLE_EXPAND,
+ DISABLE_NOTIFICATION_ICONS,
+ DISABLE_NOTIFICATION_ALERTS,
+ DISABLE_NOTIFICATION_TICKER,
+ DISABLE_SYSTEM_INFO,
+ DISABLE_HOME,
+ DISABLE_RECENT,
+ DISABLE_BACK,
+ DISABLE_CLOCK,
+ DISABLE_SEARCH
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DisableFlags {}
+
/**
* Flag to disable quick settings.
*
@@ -104,10 +120,25 @@
public static final int WINDOW_STATUS_BAR = 1;
public static final int WINDOW_NAVIGATION_BAR = 2;
+ @IntDef(flag = true, prefix = { "WINDOW_" }, value = {
+ WINDOW_STATUS_BAR,
+ WINDOW_NAVIGATION_BAR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WindowType {}
+
public static final int WINDOW_STATE_SHOWING = 0;
public static final int WINDOW_STATE_HIDING = 1;
public static final int WINDOW_STATE_HIDDEN = 2;
+ @IntDef(flag = true, prefix = { "WINDOW_STATE_" }, value = {
+ WINDOW_STATE_SHOWING,
+ WINDOW_STATE_HIDING,
+ WINDOW_STATE_HIDDEN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WindowVisibleState {}
+
public static final int CAMERA_LAUNCH_SOURCE_WIGGLE = 0;
public static final int CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = 1;
public static final int CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER = 2;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 125c4c6..0aa6a8c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4009,9 +4009,9 @@
* with the rollback manager
*
* @see #getSystemService(String)
- * @hide TODO(ruhler): hidden, @TestApi until we decide on public vs. @SystemApi.
+ * @hide
*/
- @TestApi
+ @SystemApi
public static final String ROLLBACK_SERVICE = "rollback";
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3492200..7b3497b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -27,7 +27,6 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -2268,9 +2267,9 @@
* <p class="note">This is a protected intent that can only be sent
* by the system.
*
- * @hide TODO: hidden, @TestApi until we decide on public vs. @SystemApi.
+ * @hide
*/
- @TestApi
+ @SystemApi
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_ROLLBACK_EXECUTED =
"android.intent.action.PACKAGE_ROLLBACK_EXECUTED";
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 502fb78..2978058 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -639,6 +639,21 @@
*/
public static final int PRIVATE_FLAG_HAS_FRAGILE_USER_DATA = 1 << 24;
+ /**
+ * Indicate whether this application prefers code integrity, that is, run only code that is
+ * signed. This requires android:extractNativeLibs to be "false", as well as .dex and .so (if
+ * any) stored uncompressed inside the APK, which is signed. At run time, the implications
+ * include:
+ *
+ * <ul>
+ * <li>ART will JIT the dex code directly from the APK. There may be performance characteristic
+ * changes depend on the actual workload.
+ * </ul>
+ *
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_PREFER_CODE_INTEGRITY = 1 << 25;
+
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
@@ -654,6 +669,7 @@
PRIVATE_FLAG_ISOLATED_SPLIT_LOADING,
PRIVATE_FLAG_OEM,
PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE,
+ PRIVATE_FLAG_PREFER_CODE_INTEGRITY,
PRIVATE_FLAG_PRIVILEGED,
PRIVATE_FLAG_PRODUCT,
PRIVATE_FLAG_PRODUCT_SERVICES,
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index a4ea513..64a4479b 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -277,7 +277,7 @@
in PersistableBundle appExtras, in PersistableBundle launcherExtras,
in SuspendDialogInfo dialogInfo, String callingPackage, int userId);
- boolean canSuspendPackageForUser(String packageName, int userId);
+ String[] getUnsuspendablePackagesForUser(in String[] packageNames, int userId);
boolean isPackageSuspendedForUser(String packageName, int userId);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index a2fd83f..94b7c45 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -24,7 +24,6 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.AppGlobals;
@@ -1428,9 +1427,9 @@
/**
* Request that rollbacks be enabled for the given upgrade.
- * @hide TODO: hidden, @TestApi until we decide on public vs. @SystemApi.
+ * @hide
*/
- @TestApi
+ @SystemApi
public void setEnableRollback() {
installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2608796..6110557 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5969,27 +5969,28 @@
}
/**
- * Returns whether or not a given package can be suspended via a call to {@link
+ * Returns any packages in a given set of packages that cannot be suspended via a call to {@link
* #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
* SuspendDialogInfo) setPackagesSuspended}. The platform prevents suspending certain critical
* packages to keep the device in a functioning state, e.g. the default dialer.
* Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this api.
*
* <p>
- * Note that this set of critical packages can change with time, so <em>a value of {@code true}
- * returned by this api does not guarantee that a following call to {@link
- * #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
- * SuspendDialogInfo) setPackagesSuspended} for the same package will succeed</em>, especially
- * if considerable time elapsed between the two calls.
+ * Note that this set of critical packages can change with time, so even though a package name
+ * was not returned by this call, it does not guarantee that a subsequent call to
+ * {@link #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
+ * SuspendDialogInfo) setPackagesSuspended} for that package will succeed, especially if
+ * significant time elapsed between the two calls.
*
- * @param packageName The package to check.
- * @return {@code true} if the given package can be suspended, {@code false} otherwise.
+ * @param packageNames The packages to check.
+ * @return A list of packages that can not be currently suspended by the system.
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.SUSPEND_APPS)
- public boolean canSuspendPackage(@NonNull String packageName) {
- throw new UnsupportedOperationException("canSuspendPackage not implemented");
+ @NonNull
+ public String[] getUnsuspendablePackages(@NonNull String[] packageNames) {
+ throw new UnsupportedOperationException("canSuspendPackages not implemented");
}
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e38b294..2b266b7 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -475,6 +475,7 @@
public final boolean extractNativeLibs;
public final boolean isolatedSplits;
public final boolean isSplitRequired;
+ public final boolean preferCodeIntegrity;
public ApkLite(String codePath, String packageName, String splitName,
boolean isFeatureSplit,
@@ -483,7 +484,7 @@
int revisionCode, int installLocation, List<VerifierInfo> verifiers,
SigningDetails signingDetails, boolean coreApp,
boolean debuggable, boolean multiArch, boolean use32bitAbi,
- boolean extractNativeLibs, boolean isolatedSplits) {
+ boolean preferCodeIntegrity, boolean extractNativeLibs, boolean isolatedSplits) {
this.codePath = codePath;
this.packageName = packageName;
this.splitName = splitName;
@@ -500,6 +501,7 @@
this.debuggable = debuggable;
this.multiArch = multiArch;
this.use32bitAbi = use32bitAbi;
+ this.preferCodeIntegrity = preferCodeIntegrity;
this.extractNativeLibs = extractNativeLibs;
this.isolatedSplits = isolatedSplits;
this.isSplitRequired = isSplitRequired;
@@ -1722,6 +1724,7 @@
boolean isolatedSplits = false;
boolean isFeatureSplit = false;
boolean isSplitRequired = false;
+ boolean preferCodeIntegrity = false;
String configForSplit = null;
String usesSplitName = null;
@@ -1784,6 +1787,9 @@
if ("extractNativeLibs".equals(attr)) {
extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
}
+ if ("preferCodeIntegrity".equals(attr)) {
+ preferCodeIntegrity = attrs.getAttributeBooleanValue(i, false);
+ }
}
} else if (TAG_USES_SPLIT.equals(parser.getName())) {
if (usesSplitName != null) {
@@ -1800,10 +1806,16 @@
}
}
+ if (preferCodeIntegrity && extractNativeLibs) {
+ throw new PackageParserException(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Can't request both preferCodeIntegrity and extractNativeLibs");
+ }
+
return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor,
revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable,
- multiArch, use32bitAbi, extractNativeLibs, isolatedSplits);
+ multiArch, use32bitAbi, preferCodeIntegrity, extractNativeLibs, isolatedSplits);
}
/**
@@ -3655,6 +3667,12 @@
}
if (sa.getBoolean(
+ R.styleable.AndroidManifestApplication_preferCodeIntegrity,
+ false)) {
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PREFER_CODE_INTEGRITY;
+ }
+
+ if (sa.getBoolean(
R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage,
false)) {
ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE;
diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java
index 0c05765..2040024 100644
--- a/core/java/android/content/rollback/PackageRollbackInfo.java
+++ b/core/java/android/content/rollback/PackageRollbackInfo.java
@@ -16,7 +16,7 @@
package android.content.rollback;
-import android.annotation.TestApi;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -25,9 +25,9 @@
/**
* Information about a rollback available for a particular package.
*
- * @hide TODO: hidden, @TestApi until we decide on public vs. @SystemApi.
+ * @hide
*/
-@TestApi
+@SystemApi
public final class PackageRollbackInfo implements Parcelable {
/**
* The name of a package being rolled back.
diff --git a/core/java/android/content/rollback/RollbackInfo.java b/core/java/android/content/rollback/RollbackInfo.java
index 5fa4e57..66df4fe 100644
--- a/core/java/android/content/rollback/RollbackInfo.java
+++ b/core/java/android/content/rollback/RollbackInfo.java
@@ -16,7 +16,7 @@
package android.content.rollback;
-import android.annotation.TestApi;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,9 +24,9 @@
* Information about a set of packages that can be, or already have been
* rolled back together.
*
- * @hide TODO: hidden, @TestApi until we decide on public vs. @SystemApi.
+ * @hide
*/
-@TestApi
+@SystemApi
public final class RollbackInfo implements Parcelable {
/**
@@ -39,6 +39,7 @@
// TODO: Add a flag to indicate if reboot is required, when rollback of
// staged installs is supported.
+ /** @hide */
public RollbackInfo(PackageRollbackInfo targetPackage) {
this.targetPackage = targetPackage;
}
diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java
index 294151a..c1c0bc1 100644
--- a/core/java/android/content/rollback/RollbackManager.java
+++ b/core/java/android/content/rollback/RollbackManager.java
@@ -18,8 +18,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.TestApi;
import android.content.Context;
import android.content.IntentSender;
import android.os.RemoteException;
@@ -33,12 +34,10 @@
* used to initiate rollback of those packages for a limited time period after
* upgrade.
*
- * TODO: Require an appropriate permission for apps to use these APIs.
- *
* @see PackageInstaller.SessionParams#setEnableRollback()
- * @hide TODO: hidden, @TestApi until we decide on public vs. @SystemApi.
+ * @hide
*/
-@TestApi
+@SystemApi
@SystemService(Context.ROLLBACK_SERVICE)
public final class RollbackManager {
private final String mCallerPackageName;
@@ -63,7 +62,10 @@
* @param packageName name of the package to get the availble RollbackInfo for.
* @return the rollback available for the package, or null if no rollback
* is available for the package.
+ * @throws SecurityException if the caller does not have the
+ * MANAGE_ROLLBACKS permission.
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
public @Nullable RollbackInfo getAvailableRollback(@NonNull String packageName) {
try {
return mBinder.getAvailableRollback(packageName);
@@ -78,7 +80,10 @@
* about the rollback available for a particular package.
*
* @return the names of packages that are available for rollback.
+ * @throws SecurityException if the caller does not have the
+ * MANAGE_ROLLBACKS permission.
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
public @NonNull List<String> getPackagesWithAvailableRollbacks() {
try {
return mBinder.getPackagesWithAvailableRollbacks().getList();
@@ -103,7 +108,10 @@
* rolled back from.
*
* @return the recently executed rollbacks
+ * @throws SecurityException if the caller does not have the
+ * MANAGE_ROLLBACKS permission.
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
public @NonNull List<RollbackInfo> getRecentlyExecutedRollbacks() {
try {
return mBinder.getRecentlyExecutedRollbacks().getList();
@@ -127,7 +135,10 @@
*
* @param rollback to execute
* @param statusReceiver where to deliver the results
+ * @throws SecurityException if the caller does not have the
+ * MANAGE_ROLLBACKS permission.
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
public void executeRollback(@NonNull RollbackInfo rollback,
@NonNull IntentSender statusReceiver) {
try {
@@ -143,9 +154,10 @@
* across device reboot, by simulating what happens on reboot without
* actually rebooting the device.
*
- * @hide
+ * @throws SecurityException if the caller does not have the
+ * MANAGE_ROLLBACKS permission.
*/
- @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
public void reloadPersistedData() {
try {
mBinder.reloadPersistedData();
@@ -160,10 +172,10 @@
* expiring rollback data.
*
* @param packageName the name of the package to expire data for.
- *
- * @hide
+ * @throws SecurityException if the caller does not have the
+ * MANAGE_ROLLBACKS permission.
*/
- @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
public void expireRollbackForPackage(@NonNull String packageName) {
try {
mBinder.expireRollbackForPackage(packageName);
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index a4c1332..dd782ec 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -60,6 +60,13 @@
return context.getResources().getBoolean(R.bool.config_nightDisplayAvailable);
}
+ /**
+ * Returns {@code true} if display white balance is supported by the device.
+ */
+ public static boolean isDisplayWhiteBalanceAvailable(Context context) {
+ return context.getResources().getBoolean(R.bool.config_displayWhiteBalanceAvailable);
+ }
+
private static class ColorDisplayManagerInternal {
private static ColorDisplayManagerInternal sInstance;
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index bac23b3..1630b06 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -975,14 +975,11 @@
mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
}
final String msg = getAcquiredString(mContext, acquireInfo, vendorCode);
- if (msg == null) {
- return;
- }
final int clientInfo = acquireInfo == FACE_ACQUIRED_VENDOR
? (vendorCode + FACE_ACQUIRED_VENDOR_BASE) : acquireInfo;
if (mEnrollmentCallback != null) {
mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
- } else if (mAuthenticationCallback != null) {
+ } else if (mAuthenticationCallback != null && msg != null) {
mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
}
}
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index f3810bd..d979309 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -58,7 +58,7 @@
private static final boolean DEBUG = false;
private static final String TAG = "GraphicsEnvironment";
private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
- private static final String PROPERTY_GFX_DRIVER_WHITELIST = "ro.gfx.driver.whitelist.0";
+ private static final String GUP_WHITELIST_FILENAME = "whitelist.txt";
private static final String ANGLE_RULES_FILE = "a4a_rules.json";
private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
@@ -482,7 +482,7 @@
String applicationPackageName = context.getPackageName();
String devOptInApplicationName = coreSettings.getString(
- Settings.Global.UPDATED_GFX_DRIVER_DEV_OPT_IN_APP);
+ Settings.Global.GUP_DEV_OPT_IN_APPS);
boolean devOptIn = applicationPackageName.equals(devOptInApplicationName);
boolean whitelisted = onWhitelist(context, driverPackageName, ai.packageName);
if (!devOptIn && !whitelisted) {
@@ -567,22 +567,11 @@
private static boolean onWhitelist(Context context, String driverPackageName,
String applicationPackageName) {
- String whitelistName = SystemProperties.get(PROPERTY_GFX_DRIVER_WHITELIST);
-
- // Empty whitelist implies no updatable graphics driver. Typically, the pre-installed
- // updatable graphics driver is supposed to be a place holder and contains no graphics
- // driver and whitelist.
- if (whitelistName == null || whitelistName.isEmpty()) {
- if (DEBUG) {
- Log.w(TAG, "No whitelist found.");
- }
- return false;
- }
try {
Context driverContext = context.createPackageContext(driverPackageName,
Context.CONTEXT_RESTRICTED);
AssetManager assets = driverContext.getAssets();
- InputStream stream = assets.open(whitelistName);
+ InputStream stream = assets.open(GUP_WHITELIST_FILENAME);
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
for (String packageName; (packageName = reader.readLine()) != null; ) {
if (packageName.equals(applicationPackageName)) {
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 457453f..c4e2b12 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -1338,7 +1338,7 @@
public static final String insertImage(ContentResolver cr, Bitmap source,
String title, String description) {
ContentValues values = new ContentValues();
- values.put(Images.Media.TITLE, title);
+ values.put(Images.Media.DISPLAY_NAME, title);
values.put(Images.Media.DESCRIPTION, description);
values.put(Images.Media.MIME_TYPE, "image/jpeg");
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d93985c..52effb3 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7957,6 +7957,15 @@
"night_display_last_activated_time";
/**
+ * Control whether display white balance is currently enabled.
+ * @hide
+ */
+ public static final String DISPLAY_WHITE_BALANCE_ENABLED = "display_white_balance_enabled";
+
+ private static final Validator DISPLAY_WHITE_BALANCE_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
+ /**
* Names of the service components that the current user has explicitly allowed to
* be a VR mode listener, separated by ':'.
*
@@ -8405,6 +8414,7 @@
NIGHT_DISPLAY_CUSTOM_END_TIME,
NIGHT_DISPLAY_COLOR_TEMPERATURE,
NIGHT_DISPLAY_AUTO_MODE,
+ DISPLAY_WHITE_BALANCE_ENABLED,
SYNC_PARENT_SOUNDS,
CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
SWIPE_UP_TO_SWITCH_APPS_ENABLED,
@@ -8552,6 +8562,7 @@
VALIDATORS.put(NIGHT_DISPLAY_COLOR_TEMPERATURE,
NIGHT_DISPLAY_COLOR_TEMPERATURE_VALIDATOR);
VALIDATORS.put(NIGHT_DISPLAY_AUTO_MODE, NIGHT_DISPLAY_AUTO_MODE_VALIDATOR);
+ VALIDATORS.put(DISPLAY_WHITE_BALANCE_ENABLED, DISPLAY_WHITE_BALANCE_ENABLED_VALIDATOR);
VALIDATORS.put(SYNC_PARENT_SOUNDS, SYNC_PARENT_SOUNDS_VALIDATOR);
VALIDATORS.put(CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED_VALIDATOR);
@@ -11971,11 +11982,16 @@
"angle_gl_driver_selection_values";
/**
- * App that is selected to use updated graphics driver.
+ * Apps that are selected to use Game Update Package.
* @hide
*/
- public static final String UPDATED_GFX_DRIVER_DEV_OPT_IN_APP =
- "updated_gfx_driver_dev_opt_in_app";
+ public static final String GUP_DEV_OPT_IN_APPS = "gup_dev_opt_in_apps";
+
+ /**
+ * Apps on the black list that are forbidden to useGame Update Package.
+ * @hide
+ */
+ public static final String GUP_BLACK_LIST = "gup_black_list";
/**
* Ordered GPU debug layer list for Vulkan
diff --git a/core/java/android/service/autofill/CompositeUserData.java b/core/java/android/service/autofill/CompositeUserData.java
new file mode 100644
index 0000000..2df4ddf
--- /dev/null
+++ b/core/java/android/service/autofill/CompositeUserData.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.autofill;
+
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Holds both a generic and package-specific userData used for
+ * <a href="AutofillService.html#FieldClassification">field classification</a>.
+ *
+ * @hide
+ */
+@TestApi
+public final class CompositeUserData implements FieldClassificationUserData, Parcelable {
+
+ private final UserData mGenericUserData;
+ private final UserData mPackageUserData;
+
+ private final String[] mCategories;
+ private final String[] mValues;
+
+ public CompositeUserData(@Nullable UserData genericUserData,
+ @NonNull UserData packageUserData) {
+ mGenericUserData = genericUserData;
+ mPackageUserData = packageUserData;
+
+ final String[] packageCategoryIds = mPackageUserData.getCategoryIds();
+ final String[] packageValues = mPackageUserData.getValues();
+
+ final ArrayList<String> categoryIds = new ArrayList<>(packageCategoryIds.length);
+ final ArrayList<String> values = new ArrayList<>(packageValues.length);
+
+ Collections.addAll(categoryIds, packageCategoryIds);
+ Collections.addAll(values, packageValues);
+
+ if (mGenericUserData != null) {
+ final String[] genericCategoryIds = mGenericUserData.getCategoryIds();
+ final String[] genericValues = mGenericUserData.getValues();
+ final int size = mGenericUserData.getCategoryIds().length;
+ for (int i = 0; i < size; i++) {
+ if (!categoryIds.contains(genericCategoryIds[i])) {
+ categoryIds.add(genericCategoryIds[i]);
+ values.add(genericValues[i]);
+ }
+ }
+ }
+
+ mCategories = new String[categoryIds.size()];
+ categoryIds.toArray(mCategories);
+ mValues = new String[values.size()];
+ values.toArray(mValues);
+ }
+
+ @Nullable
+ @Override
+ public String getFieldClassificationAlgorithm() {
+ final String packageDefaultAlgo = mPackageUserData.getFieldClassificationAlgorithm();
+ if (packageDefaultAlgo != null) {
+ return packageDefaultAlgo;
+ } else {
+ return mGenericUserData == null ? null :
+ mGenericUserData.getFieldClassificationAlgorithm();
+ }
+ }
+
+ @Override
+ public Bundle getDefaultFieldClassificationArgs() {
+ final Bundle packageDefaultArgs = mPackageUserData.getDefaultFieldClassificationArgs();
+ if (packageDefaultArgs != null) {
+ return packageDefaultArgs;
+ } else {
+ return mGenericUserData == null ? null :
+ mGenericUserData.getDefaultFieldClassificationArgs();
+ }
+ }
+
+ @Nullable
+ @Override
+ public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) {
+ Preconditions.checkNotNull(categoryId);
+ final ArrayMap<String, String> categoryAlgorithms = getFieldClassificationAlgorithms();
+ if (categoryAlgorithms == null || !categoryAlgorithms.containsKey(categoryId)) {
+ return null;
+ }
+ return categoryAlgorithms.get(categoryId);
+ }
+
+ @Override
+ public ArrayMap<String, String> getFieldClassificationAlgorithms() {
+ final ArrayMap<String, String> packageAlgos = mPackageUserData
+ .getFieldClassificationAlgorithms();
+ final ArrayMap<String, String> genericAlgos = mGenericUserData == null ? null :
+ mGenericUserData.getFieldClassificationAlgorithms();
+
+ ArrayMap<String, String> categoryAlgorithms = null;
+ if (packageAlgos != null || genericAlgos != null) {
+ categoryAlgorithms = new ArrayMap<>();
+ if (genericAlgos != null) {
+ categoryAlgorithms.putAll(genericAlgos);
+ }
+ if (packageAlgos != null) {
+ categoryAlgorithms.putAll(packageAlgos);
+ }
+ }
+
+ return categoryAlgorithms;
+ }
+
+ @Override
+ public ArrayMap<String, Bundle> getFieldClassificationArgs() {
+ final ArrayMap<String, Bundle> packageArgs = mPackageUserData.getFieldClassificationArgs();
+ final ArrayMap<String, Bundle> genericArgs = mGenericUserData == null ? null :
+ mGenericUserData.getFieldClassificationArgs();
+
+ ArrayMap<String, Bundle> categoryArgs = null;
+ if (packageArgs != null || genericArgs != null) {
+ categoryArgs = new ArrayMap<>();
+ if (genericArgs != null) {
+ categoryArgs.putAll(genericArgs);
+ }
+ if (packageArgs != null) {
+ categoryArgs.putAll(packageArgs);
+ }
+ }
+
+ return categoryArgs;
+ }
+
+ @Override
+ public String[] getCategoryIds() {
+ return mCategories;
+ }
+
+ @Override
+ public String[] getValues() {
+ return mValues;
+ }
+
+ /////////////////////////////////////
+ // Object "contract" methods. //
+ /////////////////////////////////////
+ @Override
+ public String toString() {
+ if (!sDebug) return super.toString();
+
+ // OK to print UserData because UserData.toString() is PII-aware
+ final StringBuilder builder = new StringBuilder("genericUserData=")
+ .append(mGenericUserData)
+ .append(", packageUserData=").append(mPackageUserData);
+ return builder.toString();
+ }
+
+ /////////////////////////////////////
+ // Parcelable "contract" methods. //
+ /////////////////////////////////////
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeParcelable(mGenericUserData, 0);
+ parcel.writeParcelable(mPackageUserData, 0);
+ }
+
+ public static final Parcelable.Creator<CompositeUserData> CREATOR =
+ new Parcelable.Creator<CompositeUserData>() {
+ @Override
+ public CompositeUserData createFromParcel(Parcel parcel) {
+ // Always go through the builder to ensure the data ingested by
+ // the system obeys the contract of the builder to avoid attacks
+ // using specially crafted parcels.
+ final UserData genericUserData = parcel.readParcelable(null);
+ final UserData packageUserData = parcel.readParcelable(null);
+ return new CompositeUserData(genericUserData, packageUserData);
+ }
+
+ @Override
+ public CompositeUserData[] newArray(int size) {
+ return new CompositeUserData[size];
+ }
+ };
+}
diff --git a/core/java/android/service/autofill/FieldClassificationUserData.java b/core/java/android/service/autofill/FieldClassificationUserData.java
new file mode 100644
index 0000000..3d6cac4
--- /dev/null
+++ b/core/java/android/service/autofill/FieldClassificationUserData.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.autofill;
+
+import android.os.Bundle;
+import android.util.ArrayMap;
+
+/**
+ * Class used to define a generic UserData for field classification
+ *
+ * @hide
+ */
+public interface FieldClassificationUserData {
+ /**
+ * Gets the name of the default algorithm that is used to calculate
+ * {@link FieldClassification.Match#getScore()} match scores}.
+ */
+ String getFieldClassificationAlgorithm();
+
+ /**
+ * Gets the default field classification args.
+ */
+ Bundle getDefaultFieldClassificationArgs();
+
+ /**
+ * Gets the name of the field classification algorithm for a specific category.
+ *
+ * @param categoryId id of the specific category.
+ */
+ String getFieldClassificationAlgorithmForCategory(String categoryId);
+
+ /**
+ * Gets all field classification algorithms for specific categories.
+ */
+ ArrayMap<String, String> getFieldClassificationAlgorithms();
+
+ /**
+ * Gets all field classification args for specific categories.
+ */
+ ArrayMap<String, Bundle> getFieldClassificationArgs();
+
+ /**
+ * Gets all category ids
+ */
+ String[] getCategoryIds();
+
+ /**
+ * Gets all string values for field classification
+ */
+ String[] getValues();
+}
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index d408e9a..93ee8c3 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -516,6 +516,9 @@
/**
* Sets a specific {@link UserData} for field classification for this request only.
*
+ * <p>Any fields in this UserData will override corresponding fields in the generic
+ * UserData object
+ *
* @return this builder
* @throws IllegalStateException if the FillResponse
* {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 37f1923..a793e09 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -48,7 +48,7 @@
* Defines the user data used for
* <a href="AutofillService.html#FieldClassification">field classification</a>.
*/
-public final class UserData implements Parcelable {
+public final class UserData implements FieldClassificationUserData, Parcelable {
private static final String TAG = "UserData";
@@ -86,11 +86,13 @@
* {@link Match#getScore()} match scores}.
*/
@Nullable
+ @Override
public String getFieldClassificationAlgorithm() {
return mDefaultAlgorithm;
}
/** @hide */
+ @Override
public Bundle getDefaultFieldClassificationArgs() {
return mDefaultArgs;
}
@@ -104,6 +106,7 @@
* @return String name of algorithm, null if none found.
*/
@Nullable
+ @Override
public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) {
Preconditions.checkNotNull(categoryId);
if (mCategoryAlgorithms == null || !mCategoryAlgorithms.containsKey(categoryId)) {
@@ -120,22 +123,26 @@
}
/** @hide */
+ @Override
public String[] getCategoryIds() {
return mCategoryIds;
}
/** @hide */
+ @Override
public String[] getValues() {
return mValues;
}
/** @hide */
@TestApi
+ @Override
public ArrayMap<String, String> getFieldClassificationAlgorithms() {
return mCategoryAlgorithms;
}
/** @hide */
+ @Override
public ArrayMap<String, Bundle> getFieldClassificationArgs() {
return mCategoryArgs;
}
diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java
index 5524506..f31ddd9 100644
--- a/core/java/android/util/Range.java
+++ b/core/java/android/util/Range.java
@@ -28,7 +28,7 @@
* "integers from 1 to 100 inclusive."
* </p>
* <p>
- * All ranges are bounded, and the left side of the range is always {@code >=}
+ * All ranges are bounded, and the left side of the range is always {@code <=}
* the right side of the range.
* </p>
*
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index a006e5d..1a708e8 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -165,7 +165,8 @@
private static native void nativeSetInputWindowInfo(long transactionObj, long nativeObject,
InputWindowHandle handle);
-
+ private static native void nativeTransferTouchFocus(long transactionObj, IBinder fromToken,
+ IBinder toToken);
private final CloseGuard mCloseGuard = CloseGuard.get();
private final String mName;
@@ -1541,6 +1542,21 @@
return this;
}
+ /**
+ * Transfers touch focus from one window to another. It is possible for multiple windows to
+ * have touch focus if they support split touch dispatch
+ * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
+ * method only transfers touch focus of the specified window without affecting
+ * other windows that may also have touch focus at the same time.
+ * @param fromToken The token of a window that currently has touch focus.
+ * @param toToken The token of the window that should receive touch focus in
+ * place of the first.
+ */
+ public Transaction transferTouchFocus(IBinder fromToken, IBinder toToken) {
+ nativeTransferTouchFocus(mNativeObject, fromToken, toToken);
+ return this;
+ }
+
@UnsupportedAppUsage
public Transaction setMatrix(SurfaceControl sc,
float dsdx, float dtdx, float dtdy, float dsdy) {
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 9f666a4..fb6cacf 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -34,6 +34,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Session used to notify a system-provided Content Capture service about events associated with
@@ -90,6 +91,12 @@
private final CloseGuard mCloseGuard = CloseGuard.get();
+ /**
+ * Guard use to ignore events after it's destroyed.
+ */
+ @NonNull
+ private final AtomicBoolean mDestroyed = new AtomicBoolean();
+
/** @hide */
@Nullable
protected final String mId = UUID.randomUUID().toString();
@@ -157,10 +164,10 @@
* <p>Once destroyed, any new notification will be dropped.
*/
public final void destroy() {
- //TODO(b/111276913): mark it as destroyed so other methods are ignored (and test on CTS)
-
- //TODO(b/111276913): probably shouldn't check for it
- if (!isContentCaptureEnabled()) return;
+ if (!mDestroyed.compareAndSet(false, true)) {
+ Log.e(mTag, "destroy(): already destroyed");
+ return;
+ }
mCloseGuard.close();
@@ -298,10 +305,13 @@
return new ViewNode.ViewStructureImpl(parentId, virtualId);
}
- abstract boolean isContentCaptureEnabled();
+ boolean isContentCaptureEnabled() {
+ return !mDestroyed.get();
+ }
@CallSuper
void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed.get());
if (mChildren != null && !mChildren.isEmpty()) {
final String prefix2 = prefix + " ";
final int numberChildren = mChildren.size();
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index ea6f2fe..12c50ce 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -219,7 +219,7 @@
/**
* Callback from {@code system_server} after call to
* {@link IContentCaptureManager#startSession(int, IBinder, ComponentName, String,
- * ContentCaptureContext, int, IResultReceiver)}.
+ * int, IResultReceiver)}.
*
* @param resultCode session state
* @param binder handle to {@code IContentCaptureDirectManager}
@@ -258,14 +258,27 @@
}
mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
}
- mEvents.add(event);
+
+ if (!mEvents.isEmpty() && event.getType() == TYPE_VIEW_TEXT_CHANGED) {
+ final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1);
+
+ // TODO(b/121045053): check if flags match
+ if (lastEvent.getType() == TYPE_VIEW_TEXT_CHANGED
+ && lastEvent.getId().equals(event.getId())) {
+ if (VERBOSE) {
+ Log.v(mTag, "Buffering VIEW_TEXT_CHANGED event, updated text = "
+ + event.getText());
+ }
+ lastEvent.setText(event.getText());
+ } else {
+ mEvents.add(event);
+ }
+ } else {
+ mEvents.add(event);
+ }
final int numberEvents = mEvents.size();
- // TODO(b/120784831): need to optimize it so we buffer changes until a number of X are
- // buffered (either total or per autofillid). For
- // example, if the user typed "a", "b", "c" and the threshold is 3, we should buffer
- // "a" and "b" then send "abc".
final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
if (bufferEvent && !forceFlush) {
@@ -412,7 +425,8 @@
@Override
boolean isContentCaptureEnabled() {
- return mSystemServerInterface != null && !mDisabled.get();
+ return super.isContentCaptureEnabled() && mSystemServerInterface != null
+ && !mDisabled.get();
}
// TODO(b/121033016): refactor "notifyXXXX" methods below to a common "Buffer" object that is
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 414cb8f..bad2dbf 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -850,7 +850,7 @@
/**
* Asynchronously evaluates JavaScript in the context of the currently displayed page.
- * If non-null, |resultCallback| will be invoked with any result returned from that
+ * If non-null, {@code resultCallback} will be invoked with any result returned from that
* execution. This method must be called on the UI thread and the callback will
* be made on the UI thread.
* <p>
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 6ab7f66..ef69b63 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -138,6 +138,20 @@
}
/**
+ * Call webview draw functor. See API in draw_fn.h.
+ * @param canvas a hardware accelerated canvas (see {@link Canvas#isHardwareAccelerated()}).
+ * @param functor created by AwDrawFn_CreateFunctor in draw_fn.h.
+ */
+ public void drawWebViewFunctor(@NonNull Canvas canvas, int functor) {
+ if (!(canvas instanceof RecordingCanvas)) {
+ // Canvas#isHardwareAccelerated() is only true for subclasses of RecordingCanvas.
+ throw new IllegalArgumentException(canvas.getClass().getName()
+ + " is not a RecordingCanvas canvas");
+ }
+ ((RecordingCanvas) canvas).drawWebViewFunctor(functor);
+ }
+
+ /**
* Detaches the draw GL functor.
*
* @param nativeDrawGLFunctor the pointer to the native functor that implements
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index e83e79b..925a589 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -357,15 +357,16 @@
}
private boolean performTtsPrompt(AlertDialog alertDialog) {
+ final String serviceName = getShortcutFeatureDescription(false /* no summary */);
final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
- if (serviceInfo == null) {
+ if (TextUtils.isEmpty(serviceName) || serviceInfo == null) {
return false;
}
if ((serviceInfo.flags & AccessibilityServiceInfo
.FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK) == 0) {
return false;
}
- final TtsPrompt tts = new TtsPrompt();
+ final TtsPrompt tts = new TtsPrompt(serviceName);
alertDialog.setOnDismissListener(dialog -> tts.dismiss());
return true;
}
@@ -378,8 +379,9 @@
private boolean mDismiss;
private TextToSpeech mTts;
- TtsPrompt() {
- mText = mContext.getString(R.string.accessibility_shortcut_spoken_feedback);
+ TtsPrompt(String serviceName) {
+ mText = mContext.getString(R.string.accessibility_shortcut_spoken_feedback,
+ serviceName);
mTts = mFrameworkObjectProvider.getTextToSpeech(mContext, this);
}
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
index 78d366c..2995a8f 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -16,14 +16,14 @@
package com.android.internal.hardware;
-import com.android.internal.R;
-
import android.content.Context;
import android.os.Build;
import android.os.SystemProperties;
import android.provider.Settings;
import android.text.TextUtils;
+import com.android.internal.R;
+
public class AmbientDisplayConfiguration {
private final Context mContext;
@@ -37,7 +37,8 @@
public boolean enabled(int user) {
return pulseOnNotificationEnabled(user)
|| pulseOnLongPressEnabled(user)
- || alwaysOnEnabled(user);
+ || alwaysOnEnabled(user)
+ || wakeLockScreenGestureEnabled(user);
}
public boolean pulseOnNotificationEnabled(int user) {
diff --git a/core/jni/android/graphics/text/LineBreaker.cpp b/core/jni/android/graphics/text/LineBreaker.cpp
index e1f2f2b..c23f1e9 100644
--- a/core/jni/android/graphics/text/LineBreaker.cpp
+++ b/core/jni/android/graphics/text/LineBreaker.cpp
@@ -16,8 +16,6 @@
#define LOG_TAG "LineBreaker"
-#include "unicode/locid.h"
-#include "unicode/brkiter.h"
#include "utils/misc.h"
#include "utils/Log.h"
#include <nativehelper/ScopedStringChars.h>
diff --git a/core/jni/android/graphics/text/MeasuredText.cpp b/core/jni/android/graphics/text/MeasuredText.cpp
index d7d96fb..68ba38b 100644
--- a/core/jni/android/graphics/text/MeasuredText.cpp
+++ b/core/jni/android/graphics/text/MeasuredText.cpp
@@ -17,8 +17,6 @@
#define LOG_TAG "MeasuredText"
#include "GraphicsJNI.h"
-#include "unicode/locid.h"
-#include "unicode/brkiter.h"
#include "utils/misc.h"
#include "utils/Log.h"
#include <nativehelper/ScopedStringChars.h>
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 29d8f30..80560f8 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1906,6 +1906,53 @@
return jStatus;
}
+static jint android_media_AudioSystem_setUidDeviceAffinities(JNIEnv *env, jobject clazz,
+ jint uid, jintArray deviceTypes, jobjectArray deviceAddresses) {
+ if (deviceTypes == nullptr || deviceAddresses == nullptr) {
+ return (jint) AUDIO_JAVA_BAD_VALUE;
+ }
+ jsize nb = env->GetArrayLength(deviceTypes);
+ if (nb == 0 || nb != env->GetArrayLength(deviceAddresses)) {
+ return (jint) AUDIO_JAVA_BAD_VALUE;
+ }
+ // retrieve all device types
+ std::vector<audio_devices_t> deviceTypesVector;
+ jint* typesPtr = nullptr;
+ typesPtr = env->GetIntArrayElements(deviceTypes, 0);
+ if (typesPtr == nullptr) {
+ return (jint) AUDIO_JAVA_BAD_VALUE;
+ }
+ for (jint i = 0; i < nb; i++) {
+ deviceTypesVector.push_back((audio_devices_t) typesPtr[i]);
+ }
+ env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0);
+
+ // check each address is a string and add device type/address to list for device affinity
+ Vector<AudioDeviceTypeAddr> deviceVector;
+ jclass stringClass = FindClassOrDie(env, "java/lang/String");
+ for (jint i = 0; i < nb; i++) {
+ jobject addrJobj = env->GetObjectArrayElement(deviceAddresses, i);
+ if (!env->IsInstanceOf(addrJobj, stringClass)) {
+ return (jint) AUDIO_JAVA_BAD_VALUE;
+ }
+ String8 address = String8(env->GetStringUTFChars((jstring) addrJobj, NULL));
+ AudioDeviceTypeAddr dev = AudioDeviceTypeAddr(typesPtr[i], address);
+ deviceVector.add(dev);
+ }
+
+ status_t status = AudioSystem::setUidDeviceAffinities((uid_t) uid, deviceVector);
+ return (jint) nativeToJavaStatus(status);
+}
+
+static jint android_media_AudioSystem_removeUidDeviceAffinities(JNIEnv *env, jobject clazz,
+ jint uid) {
+
+ //###
+ status_t status = NO_ERROR;//AudioSystem::removeUidDeviceAffinities();
+ return (jint) nativeToJavaStatus(status);
+}
+
+
static jint
android_media_AudioSystem_systemReady(JNIEnv *env, jobject thiz)
{
@@ -2133,6 +2180,10 @@
(void *)android_media_AudioSystem_getAudioHwSyncForSession},
{"registerPolicyMixes", "(Ljava/util/ArrayList;Z)I",
(void *)android_media_AudioSystem_registerPolicyMixes},
+ {"setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I",
+ (void *)android_media_AudioSystem_setUidDeviceAffinities},
+ {"removeUidDeviceAffinities", "(I)I",
+ (void *)android_media_AudioSystem_removeUidDeviceAffinities},
{"native_register_dynamic_policy_callback", "()V",
(void *)android_media_AudioSystem_registerDynPolicyCallback},
{"native_register_recording_callback", "()V",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index c745c16..6576587 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -348,6 +348,15 @@
transaction->setInputWindowInfo(ctrl, *handle->getInfo());
}
+static void nativeTransferTouchFocus(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jobject fromTokenObj, jobject toTokenObj) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+ sp<IBinder> fromToken(ibinderForJavaObject(env, fromTokenObj));
+ sp<IBinder> toToken(ibinderForJavaObject(env, toTokenObj));
+ transaction->transferTouchFocus(fromToken, toToken);
+}
+
static void nativeSetColor(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jfloatArray fColor) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -1032,7 +1041,9 @@
{"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;",
(void*)nativeCaptureLayers },
{"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V",
- (void*)nativeSetInputWindowInfo },
+ (void*)nativeSetInputWindowInfo },
+ {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)V",
+ (void*)nativeTransferTouchFocus },
{"nativeGetDisplayedContentSamplingAttributes",
"(Landroid/os/IBinder;)Landroid/hardware/display/DisplayedContentSamplingAttributes;",
(void*)nativeGetDisplayedContentSamplingAttributes },
diff --git a/core/proto/android/os/enums.proto b/core/proto/android/os/enums.proto
index db4a4c4..c357065 100644
--- a/core/proto/android/os/enums.proto
+++ b/core/proto/android/os/enums.proto
@@ -57,6 +57,7 @@
}
// These constants are defined in hardware/interfaces/thermal/1.0/types.hal
+// and in hardware/interfaces/thermal/2.0/types.hal
// They are primarily used by android/os/HardwarePropertiesManager.java.
// Any change to the types in the thermal hal should be made here as well.
enum TemperatureTypeEnum {
@@ -65,6 +66,16 @@
TEMPERATURE_TYPE_GPU = 1;
TEMPERATURE_TYPE_BATTERY = 2;
TEMPERATURE_TYPE_SKIN = 3;
+ TEMPERATURE_TYPE_USB_PORT = 4;
+ TEMPERATURE_TYPE_POWER_AMPLIFIER = 5;
+
+ // Battery Charge Limit - virtual thermal sensors.
+ TEMPERATURE_TYPE_BCL_VOLTAGE = 6;
+ TEMPERATURE_TYPE_BCL_CURRENT = 7;
+ TEMPERATURE_TYPE_BCL_PERCENTAGE = 8;
+
+ // Neural Processing Unit.
+ TEMPERATURE_TYPE_NPU = 9;
}
// Wakelock types, primarily used by android/os/PowerManager.java.
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 11bd43b..a914369 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -436,9 +436,11 @@
// Ordered GPU debug layer list for GLES
// i.e. <layer1>:<layer2>:...:<layerN>
optional SettingProto debug_layers_gles = 7;
- // App opt in to load updated graphics driver instead of
- // native graphcis driver through developer options.
- optional SettingProto updated_gfx_driver_dev_opt_in_app = 8;
+ // Apps opt in to load graphics driver from Game Update Package
+ // instead of native graphcis driver through developer options.
+ optional SettingProto gup_dev_opt_in_apps = 8;
+ // Apps on the black list that are forbidden to useGame Update Package.
+ optional SettingProto gup_black_list = 9;
}
optional Gpu gpu = 59;
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 60561bd..6f9a564 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -82,6 +82,7 @@
repeated ActivityStackProto stacks = 3;
optional int32 focused_stack_id = 4;
optional .com.android.server.wm.IdentifierProto resumed_activity = 5;
+ optional bool single_task_instance = 6;
}
message ActivityStackProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b16c16d..be37ca9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3854,6 +3854,10 @@
<permission android:name="android.permission.PACKAGE_ROLLBACK_AGENT"
android:protectionLevel="signature" />
+ <!-- @SystemApi @hide Allows managing apk level rollbacks. -->
+ <permission android:name="android.permission.MANAGE_ROLLBACKS"
+ android:protectionLevel="signature|installer" />
+
<!-- @SystemApi @hide Allows an application to mark other applications as harmful -->
<permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS"
android:protectionLevel="signature|verifier" />
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 7dee2af..ded2b35 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -31,7 +31,7 @@
android:layout_alwaysShow="true"
android:elevation="8dp"
android:paddingStart="16dp"
- android:background="@color/white" >
+ android:background="?attr/colorBackgroundFloating" >
<TextView android:id="@+id/profile_button"
android:layout_width="wrap_content"
android:layout_height="48dp"
@@ -73,7 +73,7 @@
android:id="@+id/resolver_list"
android:clipToPadding="false"
android:scrollbarStyle="outsideOverlay"
- android:background="@color/white"
+ android:background="?attr/colorBackgroundFloating"
android:elevation="8dp"
android:listSelector="@color/transparent"
android:divider="@null"
@@ -84,7 +84,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
- android:background="@color/white"
+ android:background="?attr/colorBackgroundFloating"
android:text="@string/noApplications"
android:padding="32dp"
android:gravity="center"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index dd51cb6..54f6c63 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1112,6 +1112,11 @@
resource] to be present in order to function. Default value is false. -->
<attr name="isSplitRequired" format="boolean" />
+ <!-- Flag to specify if this app prioritizes code integrity. The system may choose
+ to run with better integrity guarantee in various components if possible based on the app's
+ <code>targetSdkVersion</code>. -->
+ <attr name="preferCodeIntegrity" format="boolean" />
+
<!-- Extra options for an activity's UI. Applies to either the {@code <activity>} or
{@code <application>} tag. If specified on the {@code <application>}
tag these will be considered defaults for all activities in the
@@ -1580,6 +1585,7 @@
to honor this flag as well. -->
<attr name="usesCleartextTraffic" />
<attr name="multiArch" />
+ <attr name="preferCodeIntegrity" />
<attr name="extractNativeLibs" />
<attr name="defaultToDeviceProtectedStorage" format="boolean" />
<attr name="directBootAware" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 11cc1f5..e4abf8f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -929,6 +929,9 @@
in hardware. -->
<bool name="config_setColorTransformAccelerated">false</bool>
+ <!-- Boolean indicating whether display white balance is supported. -->
+ <bool name="config_displayWhiteBalanceAvailable">false</bool>
+
<!-- Control whether Night display is available. This should only be enabled on devices
that have a HWC implementation that can apply the matrix passed to setColorTransform
without impacting power, performance, and app compatibility (e.g. protected content). -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d383362..a7bc57a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3072,7 +3072,7 @@
<!-- Title of intent resolver dialog when selecting a viewer application that opens URI
and a previously used application is known [CHAR LIMIT=128]. -->
<string name="whichGiveAccessToApplicationNamed">Give access to open <xliff:g id="host" example="mail.google.com">%1$s</xliff:g> links with <xliff:g id="application" example="Gmail">%2$s</xliff:g></string>
- <!-- Label for a link to an intent resolver dialog to open URI [CHAR LIMIT=16] -->
+ <!-- Label for a link to an intent resolver dialog to open URI [CHAR LIMIT=18] -->
<string name="whichGiveAccessToApplicationLabel">Give access</string>
<!-- Title of intent resolver dialog when selecting an editor application to run. -->
<string name="whichEditApplication">Edit with</string>
@@ -4436,7 +4436,8 @@
<xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> off</string>
<!-- Text spoken when accessibility shortcut warning dialog is shown. [CHAR LIMIT=none] -->
- <string name="accessibility_shortcut_spoken_feedback">Use Accessibility Shortcut again to start the current accessibility feature</string>
+ <string name="accessibility_shortcut_spoken_feedback">Press and hold both volume keys for three seconds to use
+ <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g></string>
<!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. -->
<string name="accessibility_button_prompt_text">Choose a feature to use when you tap the Accessibility button:</string>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 7c95d1e..200ef2f 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -355,6 +355,30 @@
<item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
</style>
<style name="TextAppearance.DeviceDefault.Widget.Toolbar.Title" parent="TextAppearance.DeviceDefault.Widget.ActionBar.Title"/>
+ <style name="TextAppearance.DeviceDefault.Widget.Toolbar.Subtitle" parent="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle"/>
+ <style name="TextAppearance.DeviceDefault.Body1" parent="TextAppearance.Material.Body1">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Body2" parent="TextAppearance.Material.Body2">
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Subhead" parent="TextAppearance.Material.Subhead">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Headline" parent="TextAppearance.Material.Headline">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Display1" parent="TextAppearance.Material.Display1">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Title" parent="TextAppearance.Material.Title">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Caption" parent="TextAppearance.Material.Caption">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.ListItem" parent="TextAppearance.DeviceDefault.Subhead"/>
+ <style name="TextAppearance.DeviceDefault.ListItemSecondary" parent="TextAppearance.DeviceDefault.Body1"/>
<!-- Preference Styles -->
<style name="Preference.DeviceDefault" parent="Preference.Material"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index eda25b3..e8cbf66 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3028,6 +3028,7 @@
<java-symbol type="drawable" name="ic_doc_generic" />
<java-symbol type="bool" name="config_setColorTransformAccelerated" />
+ <java-symbol type="bool" name="config_displayWhiteBalanceAvailable" />
<java-symbol type="bool" name="config_nightDisplayAvailable" />
<java-symbol type="bool" name="config_allowDisablingAssistDisclosure" />
<java-symbol type="integer" name="config_defaultNightDisplayAutoMode" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 56265cc..0f4ca66 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -68,6 +68,10 @@
<item name="textAppearanceLargePopupMenu">@style/TextAppearance.DeviceDefault.Widget.PopupMenu.Large</item>
<item name="textAppearanceSmallPopupMenu">@style/TextAppearance.DeviceDefault.Widget.PopupMenu.Small</item>
+ <item name="textAppearanceListItem">@style/TextAppearance.DeviceDefault.ListItem</item>
+ <item name="textAppearanceListItemSmall">@style/TextAppearance.DeviceDefault.ListItem</item>
+ <item name="textAppearanceListItemSecondary">@style/TextAppearance.DeviceDefault.ListItemSecondary</item>
+
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonStyle">@style/Widget.DeviceDefault.Button</item>
@@ -783,7 +787,6 @@
<!-- Text styles -->
<item name="textAppearance">@style/TextAppearance.DeviceDefault</item>
<item name="textAppearanceInverse">@style/TextAppearance.DeviceDefault.Inverse</item>
-
<item name="textAppearanceLarge">@style/TextAppearance.DeviceDefault.Large</item>
<item name="textAppearanceMedium">@style/TextAppearance.DeviceDefault.Medium</item>
<item name="textAppearanceSmall">@style/TextAppearance.DeviceDefault.Small</item>
@@ -792,11 +795,12 @@
<item name="textAppearanceSmallInverse">@style/TextAppearance.DeviceDefault.Small.Inverse</item>
<item name="textAppearanceSearchResultTitle">@style/TextAppearance.DeviceDefault.SearchResult.Title</item>
<item name="textAppearanceSearchResultSubtitle">@style/TextAppearance.DeviceDefault.SearchResult.Subtitle</item>
-
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
-
<item name="textAppearanceLargePopupMenu">@style/TextAppearance.DeviceDefault.Widget.PopupMenu.Large</item>
<item name="textAppearanceSmallPopupMenu">@style/TextAppearance.DeviceDefault.Widget.PopupMenu.Small</item>
+ <item name="textAppearanceListItem">@style/TextAppearance.DeviceDefault.ListItem</item>
+ <item name="textAppearanceListItemSmall">@style/TextAppearance.DeviceDefault.ListItem</item>
+ <item name="textAppearanceListItemSecondary">@style/TextAppearance.DeviceDefault.ListItemSecondary</item>
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
@@ -1439,19 +1443,23 @@
</style>
<!-- DeviceDefault theme for a window that should look like the Settings app. -->
- <style name="Theme.DeviceDefault.Settings" parent="Theme.Material.Settings">
+ <style name="Theme.DeviceDefault.Settings" parent="Theme.DeviceDefault.Light">
+ <!-- From Theme.Material.Light.LightStatusBar -->
+ <item name="windowLightStatusBar">true</item>
+
+ <!-- From Theme.Material.Settings -->
+ <item name="homeAsUpIndicator">@drawable/ic_ab_back_material_settings</item>
+ <item name="presentationTheme">@style/Theme.Material.Settings.Dialog.Presentation</item>
+ <item name="searchDialogTheme">@style/Theme.Material.Settings.SearchBar</item>
+ <item name="panelMenuListTheme">@style/Theme.Material.Settings.CompactMenu</item>
+
<!-- action bar -->
- <item name="actionBarStyle">@style/Widget.DeviceDefault.Light.ActionBar.Solid</item>
<item name="actionBarTheme">@style/ThemeOverlay.DeviceDefault.ActionBar</item>
<item name="popupTheme">@style/ThemeOverlay.DeviceDefault.Popup.Light</item>
<!-- Color palette -->
- <item name="colorBackground">@color/background_device_default_light</item>
- <item name="colorPrimary">@color/primary_device_default_settings_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item>
<item name="colorSecondary">@color/secondary_device_default_settings_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
- <item name="colorError">@color/error_color_device_default_light</item>
<item name="colorEdgeEffect">@android:color/black</item>
<!-- Add white nav bar with divider that matches material -->
@@ -1459,24 +1467,9 @@
<item name="navigationBarColor">@android:color/white</item>
<item name="windowLightNavigationBar">true</item>
- <!-- Dialog attributes -->
- <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
- <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
-
- <!-- Text styles -->
- <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
-
<!-- Button styles -->
- <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
- <!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
- <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
-
- <!-- Toolbar attributes -->
- <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
<item name="listDivider">@color/list_divider_color_light</item>
</style>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index f8bd4e3..ac57d20 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -475,7 +475,8 @@
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS,
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
- Settings.Global.UPDATED_GFX_DRIVER_DEV_OPT_IN_APP,
+ Settings.Global.GUP_DEV_OPT_IN_APPS,
+ Settings.Global.GUP_BLACK_LIST,
Settings.Global.GPU_DEBUG_LAYER_APP,
Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
diff --git a/core/tests/featureflagtests/Android.mk b/core/tests/featureflagtests/Android.mk
index 5e518b6..ce7cb18 100644
--- a/core/tests/featureflagtests/Android.mk
+++ b/core/tests/featureflagtests/Android.mk
@@ -9,7 +9,7 @@
$(call all-java-files-under, src)
LOCAL_DX_FLAGS := --core-library
-LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib androidx.test.rules
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
LOCAL_PACKAGE_NAME := FrameworksCoreFeatureFlagTests
LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/core/tests/featureflagtests/AndroidManifest.xml b/core/tests/featureflagtests/AndroidManifest.xml
index b8ffacb..326374e 100644
--- a/core/tests/featureflagtests/AndroidManifest.xml
+++ b/core/tests/featureflagtests/AndroidManifest.xml
@@ -26,7 +26,7 @@
</application>
<instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.frameworks.coretests.featureflagtests"
android:label="Frameworks FeatureFlagUtils Tests" />
diff --git a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
index 0b1b333..3160428 100644
--- a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
+++ b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
@@ -24,10 +24,11 @@
import android.content.Context;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 7e69e3a..96798f9 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -61,6 +61,7 @@
"libstatslog",
"libutils",
"libEGL",
+ "libGLESv1_CM",
"libGLESv2",
"libGLESv3",
"libvulkan",
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index 20e77b4..5b7ae70 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -37,7 +37,8 @@
}
}
-int WebViewFunctor_create(const WebViewFunctorCallbacks& prototype, RenderMode functorMode) {
+int WebViewFunctor_create(void* data, const WebViewFunctorCallbacks& prototype,
+ RenderMode functorMode) {
if (functorMode != RenderMode::OpenGL_ES && functorMode != RenderMode::Vulkan) {
ALOGW("Unknown rendermode %d", (int)functorMode);
return -1;
@@ -47,7 +48,7 @@
ALOGW("Unable to map from GLES platform to a vulkan functor");
return -1;
}
- return WebViewFunctorManager::instance().createFunctor(prototype, functorMode);
+ return WebViewFunctorManager::instance().createFunctor(data, prototype, functorMode);
}
void WebViewFunctor_release(int functor) {
@@ -56,7 +57,9 @@
static std::atomic_int sNextId{1};
-WebViewFunctor::WebViewFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode) {
+WebViewFunctor::WebViewFunctor(void* data, const WebViewFunctorCallbacks& callbacks,
+ RenderMode functorMode)
+ : mData(data) {
mFunctor = sNextId++;
mCallbacks = callbacks;
mMode = functorMode;
@@ -66,12 +69,12 @@
destroyContext();
ATRACE_NAME("WebViewFunctor::onDestroy");
- mCallbacks.onDestroyed(mFunctor);
+ mCallbacks.onDestroyed(mFunctor, mData);
}
void WebViewFunctor::sync(const WebViewSyncData& syncData) const {
ATRACE_NAME("WebViewFunctor::sync");
- mCallbacks.onSync(mFunctor, syncData);
+ mCallbacks.onSync(mFunctor, mData, syncData);
}
void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) {
@@ -79,14 +82,14 @@
if (!mHasContext) {
mHasContext = true;
}
- mCallbacks.gles.draw(mFunctor, drawInfo);
+ mCallbacks.gles.draw(mFunctor, mData, drawInfo);
}
void WebViewFunctor::destroyContext() {
if (mHasContext) {
mHasContext = false;
ATRACE_NAME("WebViewFunctor::onContextDestroyed");
- mCallbacks.onContextDestroyed(mFunctor);
+ mCallbacks.onContextDestroyed(mFunctor, mData);
}
}
@@ -95,9 +98,9 @@
return sInstance;
}
-int WebViewFunctorManager::createFunctor(const WebViewFunctorCallbacks& callbacks,
+int WebViewFunctorManager::createFunctor(void* data, const WebViewFunctorCallbacks& callbacks,
RenderMode functorMode) {
- auto object = std::make_unique<WebViewFunctor>(callbacks, functorMode);
+ auto object = std::make_unique<WebViewFunctor>(data, callbacks, functorMode);
int id = object->id();
auto handle = object->createHandle();
{
@@ -164,4 +167,4 @@
return nullptr;
}
-} // namespace android::uirenderer
\ No newline at end of file
+} // namespace android::uirenderer
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
index 2a621dd..1719ce7 100644
--- a/libs/hwui/WebViewFunctorManager.h
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -29,7 +29,7 @@
class WebViewFunctor {
public:
- WebViewFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode);
+ WebViewFunctor(void* data, const WebViewFunctorCallbacks& callbacks, RenderMode functorMode);
~WebViewFunctor();
class Handle : public LightRefBase<Handle> {
@@ -63,6 +63,7 @@
private:
WebViewFunctorCallbacks mCallbacks;
+ void* const mData;
int mFunctor;
RenderMode mMode;
bool mHasContext = false;
@@ -73,7 +74,7 @@
public:
static WebViewFunctorManager& instance();
- int createFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode);
+ int createFunctor(void* data, const WebViewFunctorCallbacks& callbacks, RenderMode functorMode);
void releaseFunctor(int functor);
void onContextDestroyed();
void destroyFunctor(int functor);
diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h
index e5346aa..da3d06a 100644
--- a/libs/hwui/private/hwui/WebViewFunctor.h
+++ b/libs/hwui/private/hwui/WebViewFunctor.h
@@ -17,6 +17,7 @@
#ifndef FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
#define FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
+#include <cutils/compiler.h>
#include <private/hwui/DrawGlInfo.h>
namespace android::uirenderer {
@@ -27,7 +28,7 @@
};
// Static for the lifetime of the process
-RenderMode WebViewFunctor_queryPlatformRenderMode();
+ANDROID_API RenderMode WebViewFunctor_queryPlatformRenderMode();
struct WebViewSyncData {
bool applyForceDark;
@@ -35,21 +36,21 @@
struct WebViewFunctorCallbacks {
// kModeSync, called on RenderThread
- void (*onSync)(int functor, const WebViewSyncData& syncData);
+ void (*onSync)(int functor, void* data, const WebViewSyncData& syncData);
// Called when either the context is destroyed _or_ when the functor's last reference goes
// away. Will always be called with an active context and always on renderthread.
- void (*onContextDestroyed)(int functor);
+ void (*onContextDestroyed)(int functor, void* data);
// Called when the last reference to the handle goes away and the handle is considered
// irrevocably destroyed. Will always be proceeded by a call to onContextDestroyed if
// this functor had ever been drawn.
- void (*onDestroyed)(int functor);
+ void (*onDestroyed)(int functor, void* data);
union {
struct {
// Called on RenderThread. initialize is guaranteed to happen before this call
- void (*draw)(int functor, const DrawGlInfo& params);
+ void (*draw)(int functor, void* data, const DrawGlInfo& params);
} gles;
// TODO: VK support. The current DrawVkInfo is monolithic and needs to be split up for
// what params are valid on what callbacks
@@ -70,12 +71,12 @@
// Creates a new WebViewFunctor from the given prototype. The prototype is copied after
// this function returns. Caller retains full ownership of it.
// Returns -1 if the creation fails (such as an unsupported functorMode + platform mode combination)
-int WebViewFunctor_create(const WebViewFunctorCallbacks& prototype, RenderMode functorMode);
+ANDROID_API int WebViewFunctor_create(void* data, const WebViewFunctorCallbacks& prototype, RenderMode functorMode);
// May be called on any thread to signal that the functor should be destroyed.
// The functor will receive an onDestroyed when the last usage of it is released,
// and it should be considered alive & active until that point.
-void WebViewFunctor_release(int functor);
+ANDROID_API void WebViewFunctor_release(int functor);
} // namespace android::uirenderer
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 5ff8993..6a1ca5a 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -315,24 +315,24 @@
static WebViewFunctorCallbacks createMockFunctor(RenderMode mode) {
auto callbacks = WebViewFunctorCallbacks{
.onSync =
- [](int functor, const WebViewSyncData& data) {
+ [](int functor, void* client_data, const WebViewSyncData& data) {
expectOnRenderThread();
sMockFunctorCounts[functor].sync++;
},
.onContextDestroyed =
- [](int functor) {
+ [](int functor, void* client_data) {
expectOnRenderThread();
sMockFunctorCounts[functor].contextDestroyed++;
},
.onDestroyed =
- [](int functor) {
+ [](int functor, void* client_data) {
expectOnRenderThread();
sMockFunctorCounts[functor].destroyed++;
},
};
switch (mode) {
case RenderMode::OpenGL_ES:
- callbacks.gles.draw = [](int functor, const DrawGlInfo& params) {
+ callbacks.gles.draw = [](int functor, void* client_data, const DrawGlInfo& params) {
expectOnRenderThread();
sMockFunctorCounts[functor].glesDraw++;
};
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 53bf84f..1b4cf7e 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -100,8 +100,8 @@
GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas);
skiaDL.mChildFunctors.push_back(&functorDrawable);
- int functor2 = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
- RenderMode::OpenGL_ES);
+ int functor2 = WebViewFunctor_create(
+ nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
auto& counts = TestUtils::countsForFunctor(functor2);
skiaDL.mChildFunctors.push_back(
skiaDL.allocateDrawable<GLFunctorDrawable>(functor2, &dummyCanvas));
diff --git a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
index c8169af..e1fb8b7 100644
--- a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
+++ b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
@@ -27,8 +27,8 @@
using namespace android::uirenderer;
TEST(WebViewFunctor, createDestroyGLES) {
- int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
- RenderMode::OpenGL_ES);
+ int functor = WebViewFunctor_create(
+ nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
ASSERT_NE(-1, functor);
WebViewFunctor_release(functor);
TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
@@ -41,8 +41,8 @@
}
TEST(WebViewFunctor, createSyncHandleGLES) {
- int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
- RenderMode::OpenGL_ES);
+ int functor = WebViewFunctor_create(
+ nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
ASSERT_NE(-1, functor);
auto handle = WebViewFunctorManager::instance().handleFor(functor);
ASSERT_TRUE(handle);
@@ -82,8 +82,8 @@
}
TEST(WebViewFunctor, createSyncDrawGLES) {
- int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
- RenderMode::OpenGL_ES);
+ int functor = WebViewFunctor_create(
+ nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
ASSERT_NE(-1, functor);
auto handle = WebViewFunctorManager::instance().handleFor(functor);
ASSERT_TRUE(handle);
@@ -109,8 +109,8 @@
}
TEST(WebViewFunctor, contextDestroyed) {
- int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
- RenderMode::OpenGL_ES);
+ int functor = WebViewFunctor_create(
+ nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
ASSERT_NE(-1, functor);
auto handle = WebViewFunctorManager::instance().handleFor(functor);
ASSERT_TRUE(handle);
@@ -151,4 +151,4 @@
EXPECT_EQ(2, counts.glesDraw);
EXPECT_EQ(2, counts.contextDestroyed);
EXPECT_EQ(1, counts.destroyed);
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 63eefe0..30b5480 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4280,9 +4280,8 @@
* Return codes for listAudioPorts(), createAudioPatch() ...
*/
- /** @hide
- * CANDIDATE FOR PUBLIC API
- */
+ /** @hide */
+ @SystemApi
public static final int SUCCESS = AudioSystem.SUCCESS;
/**
* A default error code.
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 58fc1ab..45cde0f 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -916,6 +916,13 @@
public static native int registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register);
+ /** see AudioPolicy.setUidDeviceAffinities() */
+ public static native int setUidDeviceAffinities(int uid, @NonNull int[] types,
+ @NonNull String[] addresses);
+
+ /** see AudioPolicy.removeUidDeviceAffinities() */
+ public static native int removeUidDeviceAffinities(int uid);
+
public static native int systemReady();
public static native float getStreamVolumeDB(int stream, int index, int device);
diff --git a/media/java/android/media/Controller2Link.java b/media/java/android/media/Controller2Link.java
index 2601ff7..a62db5f 100644
--- a/media/java/android/media/Controller2Link.java
+++ b/media/java/android/media/Controller2Link.java
@@ -25,8 +25,7 @@
import java.util.Objects;
/**
- * Handles incoming commands from {@link MediaSession2} and {@link MediaLibrarySession}
- * to both {@link MediaController2} and {@link MediaBrowser2}.
+ * Handles incoming commands from {@link MediaSession2} to both {@link MediaController2}.
* @hide
*/
// @SystemApi
@@ -90,7 +89,7 @@
try {
mIController.notifyConnected(seq, connectionResult);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ throw new RuntimeException(e);
}
}
@@ -99,7 +98,7 @@
try {
mIController.notifyDisconnected(seq);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ throw new RuntimeException(e);
}
}
@@ -109,7 +108,16 @@
try {
mIController.sendSessionCommand(seq, command, args, resultReceiver);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** Interface method for IMediaController2.cancelSessionCommand */
+ public void cancelSessionCommand(int seq) {
+ try {
+ mIController.cancelSessionCommand(seq);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
}
}
@@ -133,6 +141,11 @@
mController.onSessionCommand(seq, command, args, resultReceiver);
}
+ /** Stub implementation for IMediaController2.cancelSessionCommand */
+ public void onCancelCommand(int seq) {
+ mController.onCancelCommand(seq);
+ }
+
private class Controller2Stub extends IMediaController2.Stub {
@Override
public void notifyConnected(int seq, Bundle connectionResult) {
@@ -149,5 +162,10 @@
ResultReceiver resultReceiver) {
Controller2Link.this.onSessionCommand(seq, command, args, resultReceiver);
}
+
+ @Override
+ public void cancelSessionCommand(int seq) {
+ Controller2Link.this.onCancelCommand(seq);
+ }
}
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index abd6411..9fbd7ea 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -223,6 +223,11 @@
boolean isAudioServerRunning();
+ int setUidDeviceAffinity(in IAudioPolicyCallback pcb, in int uid, in int[] deviceTypes,
+ in String[] deviceAddresses);
+
+ int removeUidDeviceAffinity(in IAudioPolicyCallback pcb, in int uid);
+
// WARNING: read warning at top of file, new methods that need to be used by native
// code via IAudioManager.h need to be added to the top section.
}
diff --git a/media/java/android/media/IMediaController2.aidl b/media/java/android/media/IMediaController2.aidl
index df34a11..ca5394f 100644
--- a/media/java/android/media/IMediaController2.aidl
+++ b/media/java/android/media/IMediaController2.aidl
@@ -33,4 +33,6 @@
void notifyDisconnected(int seq) = 1;
void sendSessionCommand(int seq, in Session2Command command, in Bundle args,
in ResultReceiver resultReceiver) = 2;
+ void cancelSessionCommand(int seq) = 3;
+ // Next Id : 4
}
diff --git a/media/java/android/media/IMediaSession2.aidl b/media/java/android/media/IMediaSession2.aidl
index f6e74cf..26e717b 100644
--- a/media/java/android/media/IMediaSession2.aidl
+++ b/media/java/android/media/IMediaSession2.aidl
@@ -34,5 +34,6 @@
void disconnect(in Controller2Link caller, int seq) = 1;
void sendSessionCommand(in Controller2Link caller, int seq, in Session2Command sessionCommand,
in Bundle args, in ResultReceiver resultReceiver) = 2;
- // Next Id : 3
+ void cancelSessionCommand(in Controller2Link caller, int seq) = 3;
+ // Next Id : 4
}
diff --git a/media/java/android/media/MediaConstants.java b/media/java/android/media/MediaConstants.java
index ffdca16d8..275b0ac 100644
--- a/media/java/android/media/MediaConstants.java
+++ b/media/java/android/media/MediaConstants.java
@@ -16,7 +16,6 @@
package android.media;
-// Code for AML only
class MediaConstants {
// Bundle key for int
static final String KEY_PID = "android.media.key.PID";
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 4ef56c8..b8381a7 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -20,6 +20,8 @@
import static android.media.MediaConstants.KEY_PACKAGE_NAME;
import static android.media.MediaConstants.KEY_PID;
import static android.media.MediaConstants.KEY_SESSION2_STUB;
+import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
+import static android.media.Session2Command.RESULT_INFO_SKIPPED;
import static android.media.Session2Token.TYPE_SESSION;
import android.annotation.NonNull;
@@ -31,6 +33,8 @@
import android.os.IBinder;
import android.os.Process;
import android.os.ResultReceiver;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import java.util.concurrent.Executor;
@@ -69,6 +73,21 @@
private Session2CommandGroup mAllowedCommands;
//@GuardedBy("mLock")
private Session2Token mConnectedToken;
+ //@GuardedBy("mLock")
+ private ArrayMap<ResultReceiver, Integer> mPendingCommands;
+ //@GuardedBy("mLock")
+ private ArraySet<Integer> mRequestedCommandSeqNumbers;
+
+ /**
+ * Create a {@link MediaController2} from the {@link Session2Token}.
+ * This connects to the session and may wake up the service if it's not available.
+ *
+ * @param context Context
+ * @param token token to connect to
+ */
+ public MediaController2(@NonNull Context context, @NonNull Session2Token token) {
+ this(context, token, context.getMainExecutor(), new ControllerCallback() {});
+ }
/**
* Create a {@link MediaController2} from the {@link Session2Token}.
@@ -77,31 +96,27 @@
* @param context Context
* @param token token to connect to
* @param executor executor to run callbacks on.
- * @param callback controller callback to receive changes in
+ * @param callback controller callback to receive changes in.
*/
- public MediaController2(@NonNull final Context context, @NonNull final Session2Token token,
- @NonNull final Executor executor, @NonNull final ControllerCallback callback) {
+ public MediaController2(@NonNull Context context, @NonNull Session2Token token,
+ @NonNull Executor executor, @NonNull ControllerCallback callback) {
if (context == null) {
throw new IllegalArgumentException("context shouldn't be null");
}
if (token == null) {
throw new IllegalArgumentException("token shouldn't be null");
}
- if (callback == null) {
- throw new IllegalArgumentException("callback shouldn't be null");
- }
- if (executor == null) {
- throw new IllegalArgumentException("executor shouldn't be null");
- }
mContext = context;
mSessionToken = token;
- mCallbackExecutor = executor;
- mCallback = callback;
+ mCallbackExecutor = (executor == null) ? context.getMainExecutor() : executor;
+ mCallback = (callback == null) ? new ControllerCallback() {} : callback;
mControllerStub = new Controller2Link(this);
// NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
mResultHandler = new Handler(context.getMainLooper());
mNextSeqNumber = 0;
+ mPendingCommands = new ArrayMap<>();
+ mRequestedCommandSeqNumbers = new ArraySet<>();
if (token.getType() == TYPE_SESSION) {
connectToSession();
@@ -116,11 +131,13 @@
if (mSessionBinder != null) {
try {
mSessionBinder.unlinkToDeath(mDeathRecipient, 0);
- mSessionBinder.disconnect(mControllerStub, mNextSeqNumber++);
- } catch (RuntimeException e) {
+ mSessionBinder.disconnect(mControllerStub, getNextSeqNumber());
+ } catch (RuntimeException e) {
// No-op
}
}
+ mPendingCommands.clear();
+ mRequestedCommandSeqNumbers.clear();
mCallbackExecutor.execute(() -> {
mCallback.onDisconnected(MediaController2.this);
});
@@ -134,9 +151,8 @@
* @param command the session command
* @param args optional arguments
* @return a token which will be sent together in {@link ControllerCallback#onCommandResult}
- * when its result is received.
+ * when its result is received.
*/
- // TODO: make cancelable.
public Object sendSessionCommand(@NonNull Session2Command command, @Nullable Bundle args) {
if (command == null) {
throw new IllegalArgumentException("command shouldn't be null");
@@ -144,26 +160,50 @@
ResultReceiver resultReceiver = new ResultReceiver(mResultHandler) {
protected void onReceiveResult(int resultCode, Bundle resultData) {
+ synchronized (mLock) {
+ mPendingCommands.remove(this);
+ }
mCallbackExecutor.execute(() -> {
mCallback.onCommandResult(MediaController2.this, this,
- command, resultData);
+ command, new Session2Command.Result(resultCode, resultData));
});
}
};
synchronized (mLock) {
if (mSessionBinder != null) {
+ int seq = getNextSeqNumber();
+ mPendingCommands.put(resultReceiver, seq);
try {
- mSessionBinder.sendSessionCommand(mControllerStub, mNextSeqNumber++,
- command, args, resultReceiver);
+ mSessionBinder.sendSessionCommand(mControllerStub, seq, command, args,
+ resultReceiver);
} catch (RuntimeException e) {
- // No-op
+ mPendingCommands.remove(resultReceiver);
+ resultReceiver.send(RESULT_ERROR_UNKNOWN_ERROR, null);
}
}
}
return resultReceiver;
}
+ /**
+ * Cancels the session command previously sent.
+ *
+ * @param token the token which is returned from {@link #sendSessionCommand}.
+ */
+ public void cancelSessionCommand(@NonNull Object token) {
+ if (token == null) {
+ throw new IllegalArgumentException("token shouldn't be null");
+ }
+ synchronized (mLock) {
+ if (mSessionBinder == null) return;
+ Integer seq = mPendingCommands.remove(token);
+ if (seq != null) {
+ mSessionBinder.cancelSessionCommand(mControllerStub, seq);
+ }
+ }
+ }
+
// Called by Controller2Link.onConnected
void onConnected(int seq, Bundle connectionResult) {
final long token = Binder.clearCallingIdentity();
@@ -213,10 +253,26 @@
@Nullable ResultReceiver resultReceiver) {
final long token = Binder.clearCallingIdentity();
try {
+ synchronized (mLock) {
+ mRequestedCommandSeqNumbers.add(seq);
+ }
mCallbackExecutor.execute(() -> {
- Bundle result = mCallback.onSessionCommand(MediaController2.this, command, args);
+ boolean isCanceled;
+ synchronized (mLock) {
+ isCanceled = !mRequestedCommandSeqNumbers.remove(seq);
+ }
+ if (isCanceled) {
+ resultReceiver.send(RESULT_INFO_SKIPPED, null);
+ return;
+ }
+ Session2Command.Result result = mCallback.onSessionCommand(
+ MediaController2.this, command, args);
if (resultReceiver != null) {
- resultReceiver.send(0, result);
+ if (result == null) {
+ throw new RuntimeException("onSessionCommand shouldn't return null");
+ } else {
+ resultReceiver.send(result.getResultCode(), result.getResultData());
+ }
}
});
} finally {
@@ -224,6 +280,18 @@
}
}
+ // Called by Controller2Link.onSessionCommand
+ void onCancelCommand(int seq) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ mRequestedCommandSeqNumbers.remove(seq);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private int getNextSeqNumber() {
synchronized (mLock) {
return mNextSeqNumber++;
@@ -258,7 +326,7 @@
* @param allowedCommands commands that's allowed by the session.
*/
public void onConnected(@NonNull MediaController2 controller,
- @NonNull Session2CommandGroup allowedCommands) { }
+ @NonNull Session2CommandGroup allowedCommands) {}
/**
* Called when the session refuses the controller or the controller is disconnected from
@@ -270,7 +338,7 @@
*
* @param controller the controller for this event
*/
- public void onDisconnected(@NonNull MediaController2 controller) { }
+ public void onDisconnected(@NonNull MediaController2 controller) {}
/**
* Called when the connected session sent a session command.
@@ -278,9 +346,11 @@
* @param controller the controller for this event
* @param command the session command
* @param args optional arguments
- * @return the result for the session command
+ * @return the result for the session command. A runtime exception will be thrown if null
+ * is returned.
*/
- public Bundle onSessionCommand(@NonNull MediaController2 controller,
+ @NonNull
+ public Session2Command.Result onSessionCommand(@NonNull MediaController2 controller,
@NonNull Session2Command command, @Nullable Bundle args) {
return null;
}
@@ -294,7 +364,6 @@
* @param result the result of the session command
*/
public void onCommandResult(@NonNull MediaController2 controller, @NonNull Object token,
- @NonNull Session2Command command, @Nullable Bundle result) {
- }
+ @NonNull Session2Command command, @NonNull Session2Command.Result result) {}
}
}
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 90cfc53..4eed12f 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -902,6 +902,7 @@
map.put(MediaStore.MediaColumns.SIZE, mFileSize);
map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
map.put(MediaStore.MediaColumns.IS_DRM, mIsDrm);
+ map.putNull(MediaStore.MediaColumns.HASH);
String resolution = null;
if (mWidth > 0 && mHeight > 0) {
@@ -934,7 +935,7 @@
}
} else if (MediaFile.isImageMimeType(mMimeType)) {
// FIXME - add DESCRIPTION
- } else if (mScanSuccess && MediaFile.isAudioMimeType(mMimeType)) {
+ } else if (MediaFile.isAudioMimeType(mMimeType)) {
map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ?
mArtist : MediaStore.UNKNOWN_STRING);
map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null &&
@@ -950,10 +951,6 @@
map.put(Audio.Media.DURATION, mDuration);
map.put(Audio.Media.COMPILATION, mCompilation);
}
- if (!mScanSuccess) {
- // force mediaprovider to not determine the media type from the mime type
- map.put(Files.FileColumns.MEDIA_TYPE, 0);
- }
}
return map;
}
@@ -1056,7 +1053,7 @@
Uri tableUri = mFilesUri;
int mediaType = FileColumns.MEDIA_TYPE_NONE;
MediaInserter inserter = mMediaInserter;
- if (mScanSuccess && !mNoMedia) {
+ if (!mNoMedia) {
if (MediaFile.isVideoMimeType(mMimeType)) {
tableUri = mVideoUri;
mediaType = FileColumns.MEDIA_TYPE_VIDEO;
@@ -1131,7 +1128,7 @@
// with squashed lower case paths
values.remove(MediaStore.MediaColumns.DATA);
- if (mScanSuccess && !mNoMedia) {
+ if (!mNoMedia) {
// Changing media type must be done as separate update
if (mediaType != entry.mMediaType) {
final ContentValues mediaTypeValues = new ContentValues();
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index d67c662..4712cdd 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -20,6 +20,8 @@
import static android.media.MediaConstants.KEY_PACKAGE_NAME;
import static android.media.MediaConstants.KEY_PID;
import static android.media.MediaConstants.KEY_SESSION2_STUB;
+import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
+import static android.media.Session2Command.RESULT_INFO_SKIPPED;
import static android.media.Session2Token.TYPE_SESSION;
import android.annotation.NonNull;
@@ -34,6 +36,8 @@
import android.os.Handler;
import android.os.Process;
import android.os.ResultReceiver;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import java.util.ArrayList;
@@ -112,7 +116,7 @@
}
@Override
- public void close() throws Exception {
+ public void close() {
try {
synchronized (MediaSession2.class) {
SESSION_ID_LIST.remove(mSessionId);
@@ -120,6 +124,7 @@
Collection<ControllerInfo> controllerInfos;
synchronized (mLock) {
controllerInfos = mConnectedControllers.values();
+ mConnectedControllers.clear();
mClosed = true;
}
for (ControllerInfo info : controllerInfos) {
@@ -131,6 +136,22 @@
}
/**
+ * Returns the session ID
+ */
+ @NonNull
+ public String getSessionId() {
+ return mSessionId;
+ }
+
+ /**
+ * Returns the {@link Session2Token} for creating {@link MediaController2}.
+ */
+ @NonNull
+ public Session2Token getSessionToken() {
+ return mSessionToken;
+ }
+
+ /**
* Broadcasts a session command to all the connected controllers
* <p>
* @param command the session command
@@ -158,7 +179,6 @@
* @return a token which will be sent together in {@link SessionCallback#onCommandResult}
* when its result is received.
*/
- // TODO: make cancelable.
public Object sendSessionCommand(@NonNull ControllerInfo controller,
@NonNull Session2Command command, @Nullable Bundle args) {
if (controller == null) {
@@ -169,9 +189,10 @@
}
ResultReceiver resultReceiver = new ResultReceiver(mResultHandler) {
protected void onReceiveResult(int resultCode, Bundle resultData) {
+ controller.receiveCommandResult(this);
mCallbackExecutor.execute(() -> {
mCallback.onCommandResult(MediaSession2.this, controller, this,
- command, resultData);
+ command, new Session2Command.Result(resultCode, resultData));
});
}
};
@@ -179,6 +200,19 @@
return resultReceiver;
}
+ /**
+ * Cancels the session command previously sent.
+ *
+ * @param controller the controller to get the session command
+ * @param token the token which is returned from {@link #sendSessionCommand}.
+ */
+ public void cancelSessionCommand(ControllerInfo controller, Object token) {
+ if (token == null) {
+ throw new IllegalArgumentException("token shouldn't be null");
+ }
+ controller.cancelSessionCommand(token);
+ }
+
boolean isClosed() {
synchronized (mLock) {
return mClosed;
@@ -296,15 +330,23 @@
// TODO: check allowed commands.
final long token = Binder.clearCallingIdentity();
try {
+ synchronized (mLock) {
+ controllerInfo.addRequestedCommandSeqNumber(seq);
+ }
+
mCallbackExecutor.execute(() -> {
- try {
- Bundle result = mCallback.onSessionCommand(
- MediaSession2.this, controllerInfo, command, args);
- if (resultReceiver != null) {
- resultReceiver.send(0, result);
+ if (!controllerInfo.removeRequestedCommandSeqNumber(seq)) {
+ resultReceiver.send(RESULT_INFO_SKIPPED, null);
+ return;
+ }
+ Session2Command.Result result = mCallback.onSessionCommand(
+ MediaSession2.this, controllerInfo, command, args);
+ if (resultReceiver != null) {
+ if (result == null) {
+ throw new RuntimeException("onSessionCommand shouldn't return null");
+ } else {
+ resultReceiver.send(result.getResultCode(), result.getResultData());
}
- } catch (RuntimeException e) {
- // Controller may be died prematurely.
}
});
} finally {
@@ -312,6 +354,24 @@
}
}
+ // Called by Session2Link.onCancelCommand
+ void onCancelCommand(final Controller2Link controller, final int seq) {
+ final ControllerInfo controllerInfo;
+ synchronized (mLock) {
+ controllerInfo = mConnectedControllers.get(controller);
+ }
+ if (controllerInfo == null) {
+ return;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ controllerInfo.removeRequestedCommandSeqNumber(seq);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
/**
* Builder for {@link MediaSession2}.
* <p>
@@ -417,7 +477,13 @@
private final RemoteUserInfo mRemoteUserInfo;
private final boolean mIsTrusted;
private final Controller2Link mControllerBinder;
+ private final Object mLock = new Object();
+ //@GuardedBy("mLock")
private int mNextSeqNumber;
+ //@GuardedBy("mLock")
+ private ArrayMap<ResultReceiver, Integer> mPendingCommands;
+ //@GuardedBy("mLock")
+ private ArraySet<Integer> mRequestedCommandSeqNumbers;
@SuppressWarnings("WeakerAccess") /* synthetic access */
Session2CommandGroup mAllowedCommands;
@@ -425,15 +491,15 @@
/**
* @param remoteUserInfo remote user info
* @param trusted {@code true} if trusted, {@code false} otherwise
- * @param controllerBinder Controller2Link. Can be {@code null} only when a
- * MediaBrowserCompat connects to MediaSessionService and ControllerInfo is
- * needed for SessionCallback#onConnected().
+ * @param controllerBinder Controller2Link for the connected controller.
*/
ControllerInfo(@NonNull RemoteUserInfo remoteUserInfo, boolean trusted,
@Nullable Controller2Link controllerBinder) {
mRemoteUserInfo = remoteUserInfo;
mIsTrusted = trusted;
mControllerBinder = controllerBinder;
+ mPendingCommands = new ArrayMap<>();
+ mRequestedCommandSeqNumbers = new ArraySet<>();
}
/**
@@ -517,16 +583,53 @@
void sendSessionCommand(Session2Command command, Bundle args,
ResultReceiver resultReceiver) {
if (mControllerBinder == null) return;
+
try {
int seq = getNextSeqNumber();
+ synchronized (mLock) {
+ mPendingCommands.put(resultReceiver, seq);
+ }
mControllerBinder.sendSessionCommand(seq, command, args, resultReceiver);
} catch (RuntimeException e) {
// Controller may be died prematurely.
+ synchronized (mLock) {
+ mPendingCommands.remove(resultReceiver);
+ }
+ resultReceiver.send(RESULT_ERROR_UNKNOWN_ERROR, null);
+ }
+ }
+
+ void cancelSessionCommand(@NonNull Object token) {
+ if (mControllerBinder == null) return;
+ Integer seq;
+ synchronized (mLock) {
+ seq = mPendingCommands.remove(token);
+ }
+ if (seq != null) {
+ mControllerBinder.cancelSessionCommand(seq);
+ }
+ }
+
+ void receiveCommandResult(ResultReceiver resultReceiver) {
+ synchronized (mLock) {
+ mPendingCommands.remove(resultReceiver);
+ }
+ }
+
+ void addRequestedCommandSeqNumber(int seq) {
+ synchronized (mLock) {
+ mRequestedCommandSeqNumbers.add(seq);
+ }
+ }
+
+ boolean removeRequestedCommandSeqNumber(int seq) {
+ synchronized (mLock) {
+ return mRequestedCommandSeqNumbers.remove(seq);
}
}
private int getNextSeqNumber() {
- synchronized (this) {
+ synchronized (mLock) {
return mNextSeqNumber++;
}
}
@@ -540,7 +643,7 @@
public abstract static class SessionCallback {
/**
* Called when a controller is created for this session. Return allowed commands for
- * controller. By default it allows all connection requests and commands.
+ * controller. By default it returns {@code null}.
* <p>
* You can reject the connection by returning {@code null}. In that case, controller
* receives {@link MediaController2.ControllerCallback#onDisconnected(MediaController2)}
@@ -553,10 +656,7 @@
@Nullable
public Session2CommandGroup onConnect(@NonNull MediaSession2 session,
@NonNull ControllerInfo controller) {
- Session2CommandGroup commands = new Session2CommandGroup.Builder()
- .addAllPredefinedCommands(Session2Command.COMMAND_VERSION_1)
- .build();
- return commands;
+ return null;
}
/**
@@ -566,7 +666,7 @@
* @param controller controller information
*/
public void onDisconnected(@NonNull MediaSession2 session,
- @NonNull ControllerInfo controller) { }
+ @NonNull ControllerInfo controller) {}
/**
* Called when a controller sent a session command.
@@ -575,9 +675,11 @@
* @param controller controller information
* @param command the session command
* @param args optional arguments
- * @return The result for the session command
+ * @return the result for the session command. A runtime exception will be thrown if null
+ * is returned.
*/
- public Bundle onSessionCommand(@NonNull MediaSession2 session,
+ @NonNull
+ public Session2Command.Result onSessionCommand(@NonNull MediaSession2 session,
@NonNull ControllerInfo controller, @NonNull Session2Command command,
@Nullable Bundle args) {
return null;
@@ -594,8 +696,6 @@
*/
public void onCommandResult(@NonNull MediaSession2 session,
@NonNull ControllerInfo controller, @NonNull Object token,
- @NonNull Session2Command command, @Nullable Bundle result) {
- }
+ @NonNull Session2Command command, @NonNull Session2Command.Result result) {}
}
}
-
diff --git a/media/java/android/media/Session2Command.java b/media/java/android/media/Session2Command.java
index a5e2ae4..d2a5aae 100644
--- a/media/java/android/media/Session2Command.java
+++ b/media/java/android/media/Session2Command.java
@@ -16,18 +16,13 @@
package android.media;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
-import android.util.ArrayMap;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
import java.util.Objects;
/**
@@ -46,368 +41,26 @@
*/
public final class Session2Command implements Parcelable {
/**
- * The first version of session commands. This version is for commands introduced in API 29.
- * <p>
- * This would be used to specify which commands should be added by
- * {@link Session2CommandGroup.Builder#addAllPredefinedCommands(int)}
- *
- * @see Session2CommandGroup.Builder#addAllPredefinedCommands(int)
- */
- public static final int COMMAND_VERSION_1 = 1;
-
- /**
- * @hide
- */
- public static final int COMMAND_VERSION_CURRENT = COMMAND_VERSION_1;
-
- /**
- * @hide
- */
- @IntDef({COMMAND_VERSION_1})
- @Retention(RetentionPolicy.SOURCE)
- public @interface CommandVersion {}
-
- /**
- * @hide
- */
- @IntDef({COMMAND_CODE_CUSTOM,
- COMMAND_CODE_PLAYER_PLAY,
- COMMAND_CODE_PLAYER_PAUSE,
- COMMAND_CODE_PLAYER_PREPARE,
- COMMAND_CODE_PLAYER_SEEK_TO,
- COMMAND_CODE_PLAYER_SET_SPEED,
- COMMAND_CODE_PLAYER_GET_PLAYLIST,
- COMMAND_CODE_PLAYER_SET_PLAYLIST,
- COMMAND_CODE_PLAYER_SKIP_TO_PLAYLIST_ITEM,
- COMMAND_CODE_PLAYER_SKIP_TO_PREVIOUS_PLAYLIST_ITEM,
- COMMAND_CODE_PLAYER_SKIP_TO_NEXT_PLAYLIST_ITEM,
- COMMAND_CODE_PLAYER_SET_SHUFFLE_MODE,
- COMMAND_CODE_PLAYER_SET_REPEAT_MODE,
- COMMAND_CODE_PLAYER_GET_PLAYLIST_METADATA,
- COMMAND_CODE_PLAYER_ADD_PLAYLIST_ITEM,
- COMMAND_CODE_PLAYER_REMOVE_PLAYLIST_ITEM,
- COMMAND_CODE_PLAYER_REPLACE_PLAYLIST_ITEM,
- COMMAND_CODE_PLAYER_GET_CURRENT_MEDIA_ITEM,
- COMMAND_CODE_PLAYER_UPDATE_LIST_METADATA,
- COMMAND_CODE_PLAYER_SET_MEDIA_ITEM,
- COMMAND_CODE_VOLUME_SET_VOLUME,
- COMMAND_CODE_VOLUME_ADJUST_VOLUME,
- COMMAND_CODE_SESSION_FAST_FORWARD,
- COMMAND_CODE_SESSION_REWIND,
- COMMAND_CODE_SESSION_SKIP_FORWARD,
- COMMAND_CODE_SESSION_SKIP_BACKWARD,
- COMMAND_CODE_SESSION_SET_RATING,
- COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT,
- COMMAND_CODE_LIBRARY_SUBSCRIBE,
- COMMAND_CODE_LIBRARY_UNSUBSCRIBE,
- COMMAND_CODE_LIBRARY_GET_CHILDREN,
- COMMAND_CODE_LIBRARY_GET_ITEM,
- COMMAND_CODE_LIBRARY_SEARCH,
- COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface CommandCode {}
-
- /**
* Command code for the custom command which can be defined by string action in the
* {@link Session2Command}.
*/
public static final int COMMAND_CODE_CUSTOM = 0;
- ////////////////////////////////////////////////////////////////////////////////////////////////
- // Player commands (i.e. commands to {@link Session2Player})
- ////////////////////////////////////////////////////////////////////////////////////////////////
- static final ArrayMap<Integer, Range> VERSION_PLAYER_COMMANDS_MAP = new ArrayMap<>();
- static final ArrayMap<Integer, Range> VERSION_PLAYER_PLAYLIST_COMMANDS_MAP = new ArrayMap<>();
-
- // TODO: check the link tag, and reassign int values properly.
/**
- * Command code for {@link MediaController2#play()}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info,
- * Session2Command)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
+ * Result code representing that the command is skipped or canceled. For an example, a seek
+ * command can be skipped if it is followed by another seek command.
*/
- public static final int COMMAND_CODE_PLAYER_PLAY = 10000;
+ public static final int RESULT_INFO_SKIPPED = 1;
/**
- * Command code for {@link MediaController2#pause()}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info,
- * Session2Command)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
+ * Result code representing that the command is successfully completed.
*/
- public static final int COMMAND_CODE_PLAYER_PAUSE = 10001;
+ public static final int RESULT_SUCCESS = 0;
/**
- * Command code for {@link MediaController2#prepare()}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info,
- * Session2Command)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
+ * Result code represents that call is ended with an unknown error.
*/
- public static final int COMMAND_CODE_PLAYER_PREPARE = 10002;
-
- /**
- * Command code for {@link MediaController2#seekTo(long)}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info,
- * Session2Command)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_PLAYER_SEEK_TO = 10003;
-
- /**
- * Command code for {@link MediaController2#setPlaybackSpeed(float)}}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link Session22Callback#onCommandRequest(MediaSession2, Controller2Info,
- * Session2Command)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_PLAYER_SET_SPEED = 10004;
-
- /**
- * Command code for {@link MediaController2#getPlaylist()}. This will expose metadata
- * information to the controller.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_PLAYER_GET_PLAYLIST = 10005;
-
- /**
- * Command code for {@link MediaController2#setPlaylist(List, MediaMetadata)}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the
- * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_PLAYER_SET_PLAYLIST = 10006;
-
- /**
- * Command code for {@link MediaController2#skipToPlaylistItem(int)}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the
- * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_PLAYER_SKIP_TO_PLAYLIST_ITEM = 10007;
-
- /**
- * Command code for {@link MediaController2#skipToPreviousPlaylistItem()}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link Session2Callback#onCommandRequest(
- * MediaSession2, Controller2Info, Session2Command)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_PLAYER_SKIP_TO_PREVIOUS_PLAYLIST_ITEM = 10008;
-
- /**
- * Command code for {@link MediaController2#skipToNextPlaylistItem()}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link Session2Callback#onCommandRequest(
- * MediaSession2, Controller2Info, Session2Command)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
-
- public static final int COMMAND_CODE_PLAYER_SKIP_TO_NEXT_PLAYLIST_ITEM = 10009;
-
- /**
- * Command code for {@link MediaController2#setShuffleMode(int)}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the
- * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_PLAYER_SET_SHUFFLE_MODE = 10010;
-
- /**
- * Command code for {@link MediaController2#setRepeatMode(int)}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the
- * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_PLAYER_SET_REPEAT_MODE = 10011;
-
- /**
- * Command code for {@link MediaController2#getPlaylistMetadata()}. This will expose metadata
- * information to the controller.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_PLAYER_GET_PLAYLIST_METADATA = 10012;
-
- /**
- * Command code for {@link MediaController2#addPlaylistItem(int, String)}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the
- * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_PLAYER_ADD_PLAYLIST_ITEM = 10013;
-
- /**
- * Command code for {@link MediaController2#removePlaylistItem(int, String)}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the
- * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_PLAYER_REMOVE_PLAYLIST_ITEM = 10014;
-
- /**
- * Command code for {@link MediaController2#replacePlaylistItem(int, String)}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the
- * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_PLAYER_REPLACE_PLAYLIST_ITEM = 10015;
-
- /**
- * Command code for {@link MediaController2#getCurrentMediaItem()}. This will expose metadata
- * information to the controller.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_PLAYER_GET_CURRENT_MEDIA_ITEM = 10016;
-
- /**
- * Command code for {@link MediaController2#updatePlaylistMetadata(MediaMetadata)}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the
- * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_PLAYER_UPDATE_LIST_METADATA = 10017;
-
- /**
- * Command code for {@link MediaController2#setMediaItem(String)}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the
- * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_PLAYER_SET_MEDIA_ITEM = 10018;
-
- static {
- VERSION_PLAYER_COMMANDS_MAP.put(COMMAND_VERSION_1,
- new Range(COMMAND_CODE_PLAYER_PLAY, COMMAND_CODE_PLAYER_SET_MEDIA_ITEM));
- }
-
- static {
- VERSION_PLAYER_PLAYLIST_COMMANDS_MAP.put(COMMAND_VERSION_1,
- new Range(COMMAND_CODE_PLAYER_GET_PLAYLIST,
- COMMAND_CODE_PLAYER_SET_MEDIA_ITEM));
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////
- // Volume commands (i.e. commands to {@link AudioManager} or {@link RouteMediaPlayer})
- ////////////////////////////////////////////////////////////////////////////////////////////////
- static final ArrayMap<Integer, Range> VERSION_VOLUME_COMMANDS_MAP = new ArrayMap<>();
-
- /**
- * Command code for {@link MediaController2#setVolumeTo(int, int)}.
- * <p>
- * <p>
- * If the session doesn't reject the request through the
- * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)},
- * command would adjust the device volume. It would send to the player directly only if it's
- * remote player. See RouteMediaPlayer for a remote player.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_VOLUME_SET_VOLUME = 30000;
-
- /**
- * Command code for {@link MediaController2#adjustVolume(int, int)}.
- * <p>
- * If the session doesn't reject the request through the
- * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)},
- * command would adjust the device volume. It would send to the player directly only if it's
- * remote player. See RouteMediaPlayer for a remote player.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_VOLUME_ADJUST_VOLUME = 30001;
-
- static {
- VERSION_VOLUME_COMMANDS_MAP.put(COMMAND_VERSION_1,
- new Range(COMMAND_CODE_VOLUME_SET_VOLUME,
- COMMAND_CODE_VOLUME_ADJUST_VOLUME));
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////
- // Session commands (i.e. commands to {@link MediaSession2#Session2Callback})
- ////////////////////////////////////////////////////////////////////////////////////////////////
- static final ArrayMap<Integer, Range> VERSION_SESSION_COMMANDS_MAP = new ArrayMap<>();
-
- /**
- * Command code for {@link MediaController2#fastForward()}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_SESSION_FAST_FORWARD = 40000;
-
- /**
- * Command code for {@link MediaController2#rewind()}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_SESSION_REWIND = 40001;
-
- /**
- * Command code for {@link MediaController2#skipForward()}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_SESSION_SKIP_FORWARD = 40002;
-
- /**
- * Command code for {@link MediaController2#skipBackward()}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_SESSION_SKIP_BACKWARD = 40003;
-
- /**
- * Command code for {@link MediaController2#setRating(String, Rating)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_SESSION_SET_RATING = 40010;
+ public static final int RESULT_ERROR_UNKNOWN_ERROR = -1;
public static final Parcelable.Creator<Session2Command> CREATOR =
new Parcelable.Creator<Session2Command>() {
@@ -422,82 +75,17 @@
}
};
- static {
- VERSION_SESSION_COMMANDS_MAP.put(COMMAND_VERSION_1,
- new Range(COMMAND_CODE_SESSION_FAST_FORWARD, COMMAND_CODE_SESSION_SET_RATING));
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////
- // Session commands (i.e. commands to {@link MediaLibrarySession#MediaLibrarySessionCallback})
- ////////////////////////////////////////////////////////////////////////////////////////////////
- static final ArrayMap<Integer, Range> VERSION_LIBRARY_COMMANDS_MAP = new ArrayMap<>();
-
- /**
- * Command code for {@link MediaBrowser2#getLibraryRoot(Library2Params)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 50000;
-
- /**
- * Command code for {@link MediaBrowser2#subscribe(String, Library2Params)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 50001;
-
- /**
- * Command code for {@link MediaBrowser2#unsubscribe(String)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 50002;
-
- /**
- * Command code for {@link MediaBrowser2#getChildren(String, int, int, Library2Params)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 50003;
-
- /**
- * Command code for {@link MediaBrowser2#getItem(String)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 50004;
-
- /**
- * Command code for {@link MediaBrowser2#search(String, LibraryParams)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_LIBRARY_SEARCH = 50005;
-
- /**
- * Command code for {@link MediaBrowser2#getSearchResult(String, int, int, Library2Params)}.
- * <p>
- * Code version is {@link #COMMAND_VERSION_1}.
- */
- public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 50006;
-
- static {
- VERSION_LIBRARY_COMMANDS_MAP.put(COMMAND_VERSION_1,
- new Range(COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT,
- COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT));
- }
-
- @CommandCode private final int mCommandCode;
+ private final int mCommandCode;
// Nonnull if it's custom command
private final String mCustomCommand;
private final Bundle mExtras;
/**
- * Constructor for creating a predefined command.
+ * Constructor for creating a command predefined in AndroidX media2.
*
- * @param commandCode A command code for predefined command.
+ * @param commandCode A command code for a command predefined in AndroidX media2.
*/
- public Session2Command(@CommandCode int commandCode) {
+ public Session2Command(int commandCode) {
if (commandCode == COMMAND_CODE_CUSTOM) {
throw new IllegalArgumentException("commandCode shouldn't be COMMAND_CODE_CUSTOM");
}
@@ -535,7 +123,7 @@
* Gets the command code of a predefined command.
* This will return {@link #COMMAND_CODE_CUSTOM} for a custom command.
*/
- public @CommandCode int getCommandCode() {
+ public int getCommandCode() {
return mCommandCode;
}
@@ -543,7 +131,8 @@
* Gets the action of a custom command.
* This will return {@code null} for a predefined command.
*/
- public @Nullable String getCustomCommand() {
+ @Nullable
+ public String getCustomCommand() {
return mCustomCommand;
}
@@ -551,7 +140,8 @@
* Gets the extra bundle of a custom command.
* This will return {@code null} for a predefined command.
*/
- public @Nullable Bundle getExtras() {
+ @Nullable
+ public Bundle getExtras() {
return mExtras;
}
@@ -582,15 +172,36 @@
return Objects.hash(mCustomCommand, mCommandCode);
}
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- static final class Range {
- public final int lower;
- public final int upper;
+ /**
+ * Contains the result of {@link Session2Command}.
+ */
+ public static final class Result {
+ private final int mResultCode;
+ private final Bundle mResultData;
- Range(int lower, int upper) {
- this.lower = lower;
- this.upper = upper;
+ /**
+ * Constructor of {@link Result}.
+ *
+ * @param resultCode result code
+ * @param resultData result data
+ */
+ public Result(int resultCode, Bundle resultData) {
+ mResultCode = resultCode;
+ mResultData = resultData;
+ }
+
+ /**
+ * Returns the result code.
+ */
+ public int getResultCode() {
+ return mResultCode;
+ }
+
+ /**
+ * Returns the result data.
+ */
+ public Bundle getResultData() {
+ return mResultData;
}
}
}
-
diff --git a/media/java/android/media/Session2CommandGroup.java b/media/java/android/media/Session2CommandGroup.java
index 18aff30..122dfb1 100644
--- a/media/java/android/media/Session2CommandGroup.java
+++ b/media/java/android/media/Session2CommandGroup.java
@@ -17,14 +17,11 @@
package android.media;
import static android.media.Session2Command.COMMAND_CODE_CUSTOM;
-import static android.media.Session2Command.COMMAND_VERSION_1;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.media.Session2Command.Range;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.ArrayMap;
import java.util.Collection;
import java.util.HashSet;
@@ -61,7 +58,7 @@
/**
* Default Constructor.
*/
- public Session2CommandGroup() { }
+ public Session2CommandGroup() {}
/**
* Creates a new Session2CommandGroup with commands copied from another object.
@@ -89,38 +86,6 @@
}
/**
- * Adds a command to this command group.
- *
- * @param command A command to add. Shouldn't be {@code null}.
- * @hide TODO remove this method
- */
- public void addCommand(@NonNull Session2Command command) {
- if (command == null) {
- throw new IllegalArgumentException("command shouldn't be null");
- }
- if (!hasCommand(command)) {
- mCommands.add(command);
- }
- }
-
- /**
- * Adds a predefined command with given {@code commandCode} to this command group.
- *
- * @param commandCode A command code to add.
- * Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
- * @hide TODO remove this method
- */
- public void addCommand(@Session2Command.CommandCode int commandCode) {
- if (commandCode == COMMAND_CODE_CUSTOM) {
- throw new IllegalArgumentException(
- "Use addCommand(Session2Command) for COMMAND_CODE_CUSTOM.");
- }
- if (!hasCommand(commandCode)) {
- mCommands.add(new Session2Command(commandCode));
- }
- }
-
- /**
* Checks whether this command group has a command that matches given {@code command}.
*
* @param command A command to find. Shouldn't be {@code null}.
@@ -138,7 +103,7 @@
* @param commandCode A command code to find.
* Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
*/
- public boolean hasCommand(@Session2Command.CommandCode int commandCode) {
+ public boolean hasCommand(int commandCode) {
if (commandCode == COMMAND_CODE_CUSTOM) {
throw new IllegalArgumentException("Use hasCommand(Command) for custom command");
}
@@ -153,7 +118,8 @@
/**
* Gets all commands of this command group.
*/
- public @NonNull Set<Session2Command> getCommands() {
+ @NonNull
+ public Set<Session2Command> getCommands() {
return new HashSet<>(mCommands);
}
@@ -191,7 +157,8 @@
*
* @param command A command to add. Shouldn't be {@code null}.
*/
- public @NonNull Builder addCommand(@NonNull Session2Command command) {
+ @NonNull
+ public Builder addCommand(@NonNull Session2Command command) {
if (command == null) {
throw new IllegalArgumentException("command shouldn't be null");
}
@@ -205,7 +172,8 @@
* @param commandCode A command code to add.
* Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
*/
- public @NonNull Builder addCommand(@Session2Command.CommandCode int commandCode) {
+ @NonNull
+ public Builder addCommand(int commandCode) {
if (commandCode == COMMAND_CODE_CUSTOM) {
throw new IllegalArgumentException(
"Use addCommand(Session2Command) for COMMAND_CODE_CUSTOM.");
@@ -215,33 +183,12 @@
}
/**
- * Adds all predefined session commands except for the commands added after the specified
- * version without default implementation. This provides convenient way to add commands
- * with implementation.
- *
- * @param version command version
- * @see Session2Command#COMMAND_VERSION_1
- * @see
- * MediaSession2.Session2Callback#onConnect
- */
- public @NonNull Builder addAllPredefinedCommands(
- @Session2Command.CommandVersion int version) {
- if (version != COMMAND_VERSION_1) {
- throw new IllegalArgumentException("Unknown command version " + version);
- }
- addAllPlayerCommands(version);
- addAllVolumeCommands(version);
- addAllSessionCommands(version);
- addAllLibraryCommands(version);
- return this;
- }
-
- /**
* Removes a command from this group which matches given {@code command}.
*
* @param command A command to find. Shouldn't be {@code null}.
*/
- public @NonNull Builder removeCommand(@NonNull Session2Command command) {
+ @NonNull
+ public Builder removeCommand(@NonNull Session2Command command) {
if (command == null) {
throw new IllegalArgumentException("command shouldn't be null");
}
@@ -255,7 +202,8 @@
* @param commandCode A command code to find.
* Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
*/
- public @NonNull Builder removeCommand(@Session2Command.CommandCode int commandCode) {
+ @NonNull
+ public Builder removeCommand(int commandCode) {
if (commandCode == COMMAND_CODE_CUSTOM) {
throw new IllegalArgumentException("commandCode shouldn't be COMMAND_CODE_CUSTOM");
}
@@ -263,59 +211,13 @@
return this;
}
- @NonNull Builder addAllPlayerCommands(@Session2Command.CommandVersion int version) {
- addCommands(version, Session2Command.VERSION_PLAYER_COMMANDS_MAP);
- return this;
- }
-
- @NonNull Builder addAllPlayerCommands(@Session2Command.CommandVersion int version,
- boolean includePlaylistCommands) {
- if (includePlaylistCommands) {
- return addAllPlayerCommands(version);
- }
- for (int i = COMMAND_VERSION_1; i <= version; i++) {
- Range include = Session2Command.VERSION_PLAYER_COMMANDS_MAP.get(i);
- Range exclude = Session2Command.VERSION_PLAYER_PLAYLIST_COMMANDS_MAP.get(i);
- for (int code = include.lower; code <= include.upper; code++) {
- if (code < exclude.lower && code > exclude.upper) {
- addCommand(code);
- }
- }
- }
- return this;
- }
-
- @NonNull Builder addAllVolumeCommands(@Session2Command.CommandVersion int version) {
- addCommands(version, Session2Command.VERSION_VOLUME_COMMANDS_MAP);
- return this;
- }
-
- @NonNull Builder addAllSessionCommands(@Session2Command.CommandVersion int version) {
- addCommands(version, Session2Command.VERSION_SESSION_COMMANDS_MAP);
- return this;
- }
-
- @NonNull Builder addAllLibraryCommands(@Session2Command.CommandVersion int version) {
- addCommands(version, Session2Command.VERSION_LIBRARY_COMMANDS_MAP);
- return this;
- }
-
- private void addCommands(
- @Session2Command.CommandVersion int version, ArrayMap<Integer, Range> map) {
- for (int i = COMMAND_VERSION_1; i <= version; i++) {
- Range range = map.get(i);
- for (int code = range.lower; code <= range.upper; code++) {
- addCommand(code);
- }
- }
- }
-
/**
* Builds {@link Session2CommandGroup}.
*
* @return a new {@link Session2CommandGroup}.
*/
- public @NonNull Session2CommandGroup build() {
+ @NonNull
+ public Session2CommandGroup build() {
return new Session2CommandGroup(mCommands);
}
}
diff --git a/media/java/android/media/Session2Link.java b/media/java/android/media/Session2Link.java
index 57c58dc..5fe558d 100644
--- a/media/java/android/media/Session2Link.java
+++ b/media/java/android/media/Session2Link.java
@@ -28,8 +28,7 @@
import java.util.Objects;
/**
- * Handles incoming commands from {@link MediaController2} and {@link MediaBrowser2}
- * to both {@link MediaSession2} and {@link MediaLibrarySession}.
+ * Handles incoming commands from {@link MediaController2} to {@link MediaSession2}.
* @hide
*/
// @SystemApi
@@ -113,7 +112,7 @@
try {
mISession.connect(caller, seq, connectionRequest);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ throw new RuntimeException(e);
}
}
@@ -122,7 +121,7 @@
try {
mISession.disconnect(caller, seq);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ throw new RuntimeException(e);
}
}
@@ -132,7 +131,16 @@
try {
mISession.sendSessionCommand(caller, seq, command, args, resultReceiver);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** Interface method for IMediaSession2.sendSessionCommand */
+ public void cancelSessionCommand(final Controller2Link caller, final int seq) {
+ try {
+ mISession.cancelSessionCommand(caller, seq);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
}
}
@@ -152,6 +160,11 @@
mSession.onSessionCommand(caller, seq, command, args, resultReceiver);
}
+ /** Stub implementation for IMediaSession2.cancelSessionCommand */
+ public void onCancelCommand(final Controller2Link caller, final int seq) {
+ mSession.onCancelCommand(caller, seq);
+ }
+
private class Session2Stub extends IMediaSession2.Stub {
@Override
public void connect(final Controller2Link caller, int seq, Bundle connectionRequest) {
@@ -168,5 +181,10 @@
final Session2Command command, final Bundle args, ResultReceiver resultReceiver) {
Session2Link.this.onSessionCommand(caller, seq, command, args, resultReceiver);
}
+
+ @Override
+ public void cancelSessionCommand(final Controller2Link caller, final int seq) {
+ Session2Link.this.onCancelCommand(caller, seq);
+ }
}
}
diff --git a/media/java/android/media/Session2Token.java b/media/java/android/media/Session2Token.java
index 552cc0f..7642faa 100644
--- a/media/java/android/media/Session2Token.java
+++ b/media/java/android/media/Session2Token.java
@@ -170,7 +170,7 @@
dest.writeString(mPackageName);
dest.writeString(mServiceName);
// TODO: Uncomment below
- //dest.writeStrongBinder(mSessionLink.getBinder());
+ //dest.writeStrongBinder(mSessionLink.asBinder());
dest.writeString(mComponentName == null ? "" : mComponentName.flattenToString());
}
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 7fb3aa6..1c6210e 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -165,6 +165,20 @@
}
/** @hide */
+ public boolean isRoutedToDevice(int deviceType, @NonNull String deviceAddress) {
+ if ((mRouteFlags & ROUTE_FLAG_RENDER) != ROUTE_FLAG_RENDER) {
+ return false;
+ }
+ if (deviceType != mDeviceSystemType) {
+ return false;
+ }
+ if (!deviceAddress.equals(mDeviceAddress)) {
+ return false;
+ }
+ return true;
+ }
+
+ /** @hide */
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 6103f557..65f3294 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.AudioAttributes;
+import android.media.AudioDeviceInfo;
import android.media.AudioFocusInfo;
import android.media.AudioFormat;
import android.media.AudioManager;
@@ -323,6 +324,80 @@
}
}
+ /**
+ * @hide
+ * Configures the audio framework so that all audio stream originating from the given UID
+ * can only come from a set of audio devices.
+ * For this routing to be operational, a number of {@link AudioMix} instances must have been
+ * previously registered on this policy, and routed to a super-set of the given audio devices
+ * with {@link AudioMix.Builder#setDevice(android.media.AudioDeviceInfo)}. Note that having
+ * multiple devices in the list doesn't imply the signals will be duplicated on the different
+ * audio devices, final routing will depend on the {@link AudioAttributes} of the sounds being
+ * played.
+ * @param uid UID of the application to affect.
+ * @param devices list of devices to which the audio stream of the application may be routed.
+ * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
+ * otherwise.
+ */
+ @SystemApi
+ public int setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) {
+ if (devices == null) {
+ throw new IllegalArgumentException("Illegal null list of audio devices");
+ }
+ synchronized (mLock) {
+ if (mStatus != POLICY_STATUS_REGISTERED) {
+ throw new IllegalStateException("Cannot use unregistered AudioPolicy");
+ }
+ final int[] deviceTypes = new int[devices.size()];
+ final String[] deviceAdresses = new String[devices.size()];
+ int i = 0;
+ for (AudioDeviceInfo device : devices) {
+ if (device == null) {
+ throw new IllegalArgumentException(
+ "Illegal null AudioDeviceInfo in setUidDeviceAffinity");
+ }
+ deviceTypes[i] =
+ AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType());
+ deviceAdresses[i] = device.getAddress();
+ i++;
+ }
+ final IAudioService service = getService();
+ try {
+ final int status = service.setUidDeviceAffinity(this.cb(),
+ uid, deviceTypes, deviceAdresses);
+ return status;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in setUidDeviceAffinity", e);
+ return AudioManager.ERROR;
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * Removes audio device affinity previously set by
+ * {@link #setUidDeviceAffinity(int, java.util.List)}.
+ * @param uid UID of the application affected.
+ * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
+ * otherwise.
+ */
+ @SystemApi
+ public int removeUidDeviceAffinity(int uid) {
+ synchronized (mLock) {
+ if (mStatus != POLICY_STATUS_REGISTERED) {
+ throw new IllegalStateException("Cannot use unregistered AudioPolicy");
+ }
+ final IAudioService service = getService();
+ try {
+ final int status = service.removeUidDeviceAffinity(this.cb(), uid);
+ return status;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in removeUidDeviceAffinity", e);
+ return AudioManager.ERROR;
+ }
+ }
+ }
+
public void setRegistration(String regId) {
synchronized (mLock) {
mRegistrationId = regId;
diff --git a/media/java/android/media/session/ControllerCallbackLink.java b/media/java/android/media/session/ControllerCallbackLink.java
deleted file mode 100644
index 19da7ce..0000000
--- a/media/java/android/media/session/ControllerCallbackLink.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.session;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.media.AudioAttributes;
-import android.media.MediaMetadata;
-import android.media.session.MediaSession.QueueItem;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-
-import java.util.List;
-
-/**
- * Handles incoming commands to {@link MediaController.Callback}.
- * @hide
- */
-@SystemApi
-public final class ControllerCallbackLink implements Parcelable {
- CallbackStub mCallbackStub;
- ISessionControllerCallback mIControllerCallback;
-
- /**
- * Creator for stub (Callee)
- */
- public ControllerCallbackLink(@NonNull CallbackStub callbackStub) {
- mCallbackStub = callbackStub;
- mIControllerCallback = new CallbackStubProxy();
- }
-
- /**
- * Creator for interface (Caller)
- */
- ControllerCallbackLink(Parcel in) {
- mCallbackStub = null;
- mIControllerCallback = ISessionControllerCallback.Stub.asInterface(in.readStrongBinder());
- }
-
- /** Interface method for ISessionControllerCallback.notifySessionDestroyed */
- public void notifySessionDestroyed() {
- try {
- mIControllerCallback.notifySessionDestroyed();
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionControllerCallback.notifyEvent */
- public void notifyEvent(String event, Bundle extras) {
- try {
- mIControllerCallback.notifyEvent(event, extras);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionControllerCallback.notifyPlaybackStateChanged */
- public void notifyPlaybackStateChanged(PlaybackState state) {
- try {
- mIControllerCallback.notifyPlaybackStateChanged(state);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionControllerCallback.notifyMetadataChanged */
- public void notifyMetadataChanged(MediaMetadata metadata) {
- try {
- mIControllerCallback.notifyMetadataChanged(metadata);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionControllerCallback.notifyQueueChanged */
- public void notifyQueueChanged(List<QueueItem> queue) {
- try {
- mIControllerCallback.notifyQueueChanged(queue);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionControllerCallback.notifyQueueTitleChanged */
- public void notifyQueueTitleChanged(CharSequence title) {
- try {
- mIControllerCallback.notifyQueueTitleChanged(title);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionControllerCallback.notifyExtrasChanged */
- public void notifyExtrasChanged(Bundle extras) {
- try {
- mIControllerCallback.notifyExtrasChanged(extras);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionControllerCallback.notifyVolumeInfoChanged */
- public void notifyVolumeInfoChanged(int volumeType, AudioAttributes attrs, int controlType,
- int maxVolume, int currentVolume) {
- try {
- mIControllerCallback.notifyVolumeInfoChanged(volumeType, attrs, controlType, maxVolume,
- currentVolume);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Gets the binder */
- public IBinder getBinder() {
- return mIControllerCallback.asBinder();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeStrongBinder(mIControllerCallback.asBinder());
- }
-
- public static final Parcelable.Creator<ControllerCallbackLink> CREATOR =
- new Parcelable.Creator<ControllerCallbackLink>() {
- @Override
- public ControllerCallbackLink createFromParcel(Parcel in) {
- return new ControllerCallbackLink(in);
- }
-
- @Override
- public ControllerCallbackLink[] newArray(int size) {
- return new ControllerCallbackLink[size];
- }
- };
-
- /**
- * Class for Stub implementation
- */
- public abstract static class CallbackStub {
- /** Stub method for ISessionControllerCallback.notifySessionDestroyed */
- public void onSessionDestroyed() {
- }
-
- /** Stub method for ISessionControllerCallback.notifyEvent */
- public void onEvent(String event, Bundle extras) {
- }
-
- /** Stub method for ISessionControllerCallback.notifyPlaybackStateChanged */
- public void onPlaybackStateChanged(PlaybackState state) {
- }
-
- /** Stub method for ISessionControllerCallback.notifyMetadataChanged */
- public void onMetadataChanged(MediaMetadata metadata) {
- }
-
- /** Stub method for ISessionControllerCallback.notifyQueueChanged */
- public void onQueueChanged(List<QueueItem> queue) {
- }
-
- /** Stub method for ISessionControllerCallback.notifyQueueTitleChanged */
- public void onQueueTitleChanged(CharSequence title) {
- }
-
- /** Stub method for ISessionControllerCallback.notifyExtrasChanged */
- public void onExtrasChanged(Bundle extras) {
- }
-
- /** Stub method for ISessionControllerCallback.notifyVolumeInfoChanged */
- public void onVolumeInfoChanged(int volumeType, AudioAttributes attrs, int controlType,
- int maxVolume, int currentVolume) {
- }
- }
-
- private class CallbackStubProxy extends ISessionControllerCallback.Stub {
- @Override
- public void notifyEvent(String event, Bundle extras) {
- mCallbackStub.onEvent(event, extras);
- }
-
- @Override
- public void notifySessionDestroyed() {
- mCallbackStub.onSessionDestroyed();
- }
-
- @Override
- public void notifyPlaybackStateChanged(PlaybackState state) {
- mCallbackStub.onPlaybackStateChanged(state);
- }
-
- @Override
- public void notifyMetadataChanged(MediaMetadata metadata) {
- mCallbackStub.onMetadataChanged(metadata);
- }
-
- @Override
- public void notifyQueueChanged(List<QueueItem> queue) {
- mCallbackStub.onQueueChanged(queue);
- }
-
- @Override
- public void notifyQueueTitleChanged(CharSequence title) {
- mCallbackStub.onQueueTitleChanged(title);
- }
-
- @Override
- public void notifyExtrasChanged(Bundle extras) {
- mCallbackStub.onExtrasChanged(extras);
- }
-
- @Override
- public void notifyVolumeInfoChanged(int volumeType, AudioAttributes attrs, int controlType,
- int maxVolume, int currentVolume) {
- mCallbackStub.onVolumeInfoChanged(volumeType, attrs, controlType, maxVolume,
- currentVolume);
- }
- }
-}
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 5cf6dfe..bfc05fa 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -16,6 +16,7 @@
package android.media.session;
import android.app.PendingIntent;
+import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.MediaMetadata;
import android.media.session.ISessionController;
@@ -40,7 +41,7 @@
// These commands are for the TransportPerformer
void setMetadata(in MediaMetadata metadata, long duration, String metadataDescription);
void setPlaybackState(in PlaybackState state);
- void setQueue(in List<MediaSession.QueueItem> queue);
+ void setQueue(in ParceledListSlice queue);
void setQueueTitle(CharSequence title);
void setExtras(in Bundle extras);
void setRatingType(int type);
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 9b86bfc..626338d 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -17,7 +17,7 @@
import android.content.Intent;
import android.media.Rating;
-import android.media.session.ControllerCallbackLink;
+import android.media.session.ISessionControllerCallback;
import android.net.Uri;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -26,46 +26,46 @@
* @hide
*/
oneway interface ISessionCallback {
- void notifyCommand(String packageName, int pid, int uid, in ControllerCallbackLink caller,
+ void onCommand(String packageName, int pid, int uid, ISessionControllerCallback caller,
String command, in Bundle args, in ResultReceiver cb);
- void notifyMediaButton(String packageName, int pid, int uid, in Intent mediaButtonIntent,
+ void onMediaButton(String packageName, int pid, int uid, in Intent mediaButtonIntent,
int sequenceNumber, in ResultReceiver cb);
- void notifyMediaButtonFromController(String packageName, int pid, int uid,
- in ControllerCallbackLink caller, in Intent mediaButtonIntent);
+ void onMediaButtonFromController(String packageName, int pid, int uid,
+ ISessionControllerCallback caller, in Intent mediaButtonIntent);
// These callbacks are for the TransportPerformer
- void notifyPrepare(String packageName, int pid, int uid, in ControllerCallbackLink caller);
- void notifyPrepareFromMediaId(String packageName, int pid, int uid,
- in ControllerCallbackLink caller, String mediaId, in Bundle extras);
- void notifyPrepareFromSearch(String packageName, int pid, int uid,
- in ControllerCallbackLink caller, String query, in Bundle extras);
- void notifyPrepareFromUri(String packageName, int pid, int uid,
- in ControllerCallbackLink caller, in Uri uri, in Bundle extras);
- void notifyPlay(String packageName, int pid, int uid, in ControllerCallbackLink caller);
- void notifyPlayFromMediaId(String packageName, int pid, int uid,
- in ControllerCallbackLink caller, String mediaId, in Bundle extras);
- void notifyPlayFromSearch(String packageName, int pid, int uid,
- in ControllerCallbackLink caller, String query, in Bundle extras);
- void notifyPlayFromUri(String packageName, int pid, int uid, in ControllerCallbackLink caller,
+ void onPrepare(String packageName, int pid, int uid, ISessionControllerCallback caller);
+ void onPrepareFromMediaId(String packageName, int pid, int uid,
+ ISessionControllerCallback caller, String mediaId, in Bundle extras);
+ void onPrepareFromSearch(String packageName, int pid, int uid,
+ ISessionControllerCallback caller, String query, in Bundle extras);
+ void onPrepareFromUri(String packageName, int pid, int uid, ISessionControllerCallback caller,
in Uri uri, in Bundle extras);
- void notifySkipToTrack(String packageName, int pid, int uid, in ControllerCallbackLink caller,
+ void onPlay(String packageName, int pid, int uid, ISessionControllerCallback caller);
+ void onPlayFromMediaId(String packageName, int pid, int uid, ISessionControllerCallback caller,
+ String mediaId, in Bundle extras);
+ void onPlayFromSearch(String packageName, int pid, int uid, ISessionControllerCallback caller,
+ String query, in Bundle extras);
+ void onPlayFromUri(String packageName, int pid, int uid, ISessionControllerCallback caller,
+ in Uri uri, in Bundle extras);
+ void onSkipToTrack(String packageName, int pid, int uid, ISessionControllerCallback caller,
long id);
- void notifyPause(String packageName, int pid, int uid, in ControllerCallbackLink caller);
- void notifyStop(String packageName, int pid, int uid, in ControllerCallbackLink caller);
- void notifyNext(String packageName, int pid, int uid, in ControllerCallbackLink caller);
- void notifyPrevious(String packageName, int pid, int uid, in ControllerCallbackLink caller);
- void notifyFastForward(String packageName, int pid, int uid, in ControllerCallbackLink caller);
- void notifyRewind(String packageName, int pid, int uid, in ControllerCallbackLink caller);
- void notifySeekTo(String packageName, int pid, int uid, in ControllerCallbackLink caller,
+ void onPause(String packageName, int pid, int uid, ISessionControllerCallback caller);
+ void onStop(String packageName, int pid, int uid, ISessionControllerCallback caller);
+ void onNext(String packageName, int pid, int uid, ISessionControllerCallback caller);
+ void onPrevious(String packageName, int pid, int uid, ISessionControllerCallback caller);
+ void onFastForward(String packageName, int pid, int uid, ISessionControllerCallback caller);
+ void onRewind(String packageName, int pid, int uid, ISessionControllerCallback caller);
+ void onSeekTo(String packageName, int pid, int uid, ISessionControllerCallback caller,
long pos);
- void notifyRate(String packageName, int pid, int uid, in ControllerCallbackLink caller,
+ void onRate(String packageName, int pid, int uid, ISessionControllerCallback caller,
in Rating rating);
- void notifyCustomAction(String packageName, int pid, int uid, in ControllerCallbackLink caller,
+ void onCustomAction(String packageName, int pid, int uid, ISessionControllerCallback caller,
String action, in Bundle args);
// These callbacks are for volume handling
- void notifyAdjustVolume(String packageName, int pid, int uid, in ControllerCallbackLink caller,
+ void onAdjustVolume(String packageName, int pid, int uid, ISessionControllerCallback caller,
int direction);
- void notifySetVolumeTo(String packageName, int pid, int uid,
- in ControllerCallbackLink caller, int value);
+ void onSetVolumeTo(String packageName, int pid, int uid,
+ ISessionControllerCallback caller, int value);
}
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 5c1915b..e61bf5b 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -17,9 +17,10 @@
import android.app.PendingIntent;
import android.content.Intent;
+import android.content.pm.ParceledListSlice;
import android.media.MediaMetadata;
import android.media.Rating;
-import android.media.session.ControllerCallbackLink;
+import android.media.session.ISessionControllerCallback;
import android.media.session.MediaSession;
import android.media.session.ParcelableVolumeInfo;
import android.media.session.PlaybackState;
@@ -35,52 +36,53 @@
* @hide
*/
interface ISessionController {
- void sendCommand(String packageName, in ControllerCallbackLink caller,
+ void sendCommand(String packageName, ISessionControllerCallback caller,
String command, in Bundle args, in ResultReceiver cb);
- boolean sendMediaButton(String packageName, in ControllerCallbackLink caller,
+ boolean sendMediaButton(String packageName, ISessionControllerCallback caller,
boolean asSystemService, in KeyEvent mediaButton);
- void registerCallbackListener(String packageName, in ControllerCallbackLink cb);
- void unregisterCallbackListener(in ControllerCallbackLink cb);
+ void registerCallbackListener(String packageName, ISessionControllerCallback cb);
+ void unregisterCallbackListener(ISessionControllerCallback cb);
boolean isTransportControlEnabled();
String getPackageName();
String getTag();
PendingIntent getLaunchPendingIntent();
long getFlags();
ParcelableVolumeInfo getVolumeAttributes();
- void adjustVolume(String packageName, in ControllerCallbackLink caller,
- boolean asSystemService, int direction, int flags);
- void setVolumeTo(String packageName, in ControllerCallbackLink caller,
+ void adjustVolume(String packageName, String opPackageName,
+ in ISessionControllerCallback caller, boolean asSystemService, int direction,
+ int flags);
+ void setVolumeTo(String packageName, String opPackageName, in ISessionControllerCallback caller,
int value, int flags);
// These commands are for the TransportControls
- void prepare(String packageName, in ControllerCallbackLink caller);
- void prepareFromMediaId(String packageName, in ControllerCallbackLink caller,
+ void prepare(String packageName, ISessionControllerCallback caller);
+ void prepareFromMediaId(String packageName, ISessionControllerCallback caller,
String mediaId, in Bundle extras);
- void prepareFromSearch(String packageName, in ControllerCallbackLink caller,
+ void prepareFromSearch(String packageName, ISessionControllerCallback caller,
String string, in Bundle extras);
- void prepareFromUri(String packageName, in ControllerCallbackLink caller,
+ void prepareFromUri(String packageName, ISessionControllerCallback caller,
in Uri uri, in Bundle extras);
- void play(String packageName, in ControllerCallbackLink caller);
- void playFromMediaId(String packageName, in ControllerCallbackLink caller,
+ void play(String packageName, ISessionControllerCallback caller);
+ void playFromMediaId(String packageName, ISessionControllerCallback caller,
String mediaId, in Bundle extras);
- void playFromSearch(String packageName, in ControllerCallbackLink caller,
+ void playFromSearch(String packageName, ISessionControllerCallback caller,
String string, in Bundle extras);
- void playFromUri(String packageName, in ControllerCallbackLink caller,
+ void playFromUri(String packageName, ISessionControllerCallback caller,
in Uri uri, in Bundle extras);
- void skipToQueueItem(String packageName, in ControllerCallbackLink caller, long id);
- void pause(String packageName, in ControllerCallbackLink caller);
- void stop(String packageName, in ControllerCallbackLink caller);
- void next(String packageName, in ControllerCallbackLink caller);
- void previous(String packageName, in ControllerCallbackLink caller);
- void fastForward(String packageName, in ControllerCallbackLink caller);
- void rewind(String packageName, in ControllerCallbackLink caller);
- void seekTo(String packageName, in ControllerCallbackLink caller, long pos);
- void rate(String packageName, in ControllerCallbackLink caller, in Rating rating);
- void sendCustomAction(String packageName, in ControllerCallbackLink caller,
+ void skipToQueueItem(String packageName, ISessionControllerCallback caller, long id);
+ void pause(String packageName, ISessionControllerCallback caller);
+ void stop(String packageName, ISessionControllerCallback caller);
+ void next(String packageName, ISessionControllerCallback caller);
+ void previous(String packageName, ISessionControllerCallback caller);
+ void fastForward(String packageName, ISessionControllerCallback caller);
+ void rewind(String packageName, ISessionControllerCallback caller);
+ void seekTo(String packageName, ISessionControllerCallback caller, long pos);
+ void rate(String packageName, ISessionControllerCallback caller, in Rating rating);
+ void sendCustomAction(String packageName, ISessionControllerCallback caller,
String action, in Bundle args);
MediaMetadata getMetadata();
PlaybackState getPlaybackState();
- List<MediaSession.QueueItem> getQueue();
+ ParceledListSlice getQueue();
CharSequence getQueueTitle();
Bundle getExtras();
int getRatingType();
diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
index 2f86c6c..cf31767 100644
--- a/media/java/android/media/session/ISessionControllerCallback.aidl
+++ b/media/java/android/media/session/ISessionControllerCallback.aidl
@@ -15,26 +15,25 @@
package android.media.session;
-import android.media.AudioAttributes;
+import android.content.pm.ParceledListSlice;
import android.media.MediaMetadata;
-import android.media.session.MediaSession;
import android.media.session.ParcelableVolumeInfo;
import android.media.session.PlaybackState;
+import android.media.session.MediaSession;
import android.os.Bundle;
/**
* @hide
*/
oneway interface ISessionControllerCallback {
- void notifyEvent(String event, in Bundle extras);
- void notifySessionDestroyed();
+ void onEvent(String event, in Bundle extras);
+ void onSessionDestroyed();
// These callbacks are for the TransportController
- void notifyPlaybackStateChanged(in PlaybackState state);
- void notifyMetadataChanged(in MediaMetadata metadata);
- void notifyQueueChanged(in List<MediaSession.QueueItem> queue);
- void notifyQueueTitleChanged(CharSequence title);
- void notifyExtrasChanged(in Bundle extras);
- void notifyVolumeInfoChanged(int volumeType, in AudioAttributes attrs, int controlType,
- int maxVolume, int currentVolume);
+ void onPlaybackStateChanged(in PlaybackState state);
+ void onMetadataChanged(in MediaMetadata metadata);
+ void onQueueChanged(in ParceledListSlice queue);
+ void onQueueTitleChanged(CharSequence title);
+ void onExtrasChanged(in Bundle extras);
+ void onVolumeInfoChanged(in ParcelableVolumeInfo info);
}
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 5801967..d6c226f 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -22,7 +22,7 @@
import android.media.session.IOnMediaKeyListener;
import android.media.session.IOnVolumeKeyLongPressListener;
import android.media.session.ISession;
-import android.media.session.SessionCallbackLink;
+import android.media.session.ISessionCallback;
import android.os.Bundle;
import android.view.KeyEvent;
@@ -31,13 +31,14 @@
* @hide
*/
interface ISessionManager {
- ISession createSession(String packageName, in SessionCallbackLink cb, String tag, int userId);
+ ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
List<IBinder> getSessions(in ComponentName compName, int userId);
void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent,
boolean needWakeLock);
- void dispatchVolumeKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent,
- int stream, boolean musicOnly);
- void dispatchAdjustVolume(String packageName, int suggestedStream, int delta, int flags);
+ void dispatchVolumeKeyEvent(String packageName, String opPackageName, boolean asSystemService,
+ in KeyEvent keyEvent, int stream, boolean musicOnly);
+ void dispatchAdjustVolume(String packageName, String opPackageName, int suggestedStream,
+ int delta, int flags);
void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName,
int userId);
void removeSessionsListener(in IActiveSessionsListener listener);
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 5eb77f9..181ee53 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -21,12 +21,12 @@
import android.annotation.UnsupportedAppUsage;
import android.app.PendingIntent;
import android.content.Context;
+import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
-import android.media.session.MediaSession.QueueItem;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -70,8 +70,7 @@
private final MediaSession.Token mToken;
private final Context mContext;
- private final ControllerCallbackLink mCbStub =
- new ControllerCallbackLink(new CallbackStub(this));
+ private final CallbackStub mCbStub = new CallbackStub(this);
private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
private final Object mLock = new Object();
@@ -154,7 +153,7 @@
return false;
}
try {
- return mSessionBinder.sendMediaButton(mContext.getOpPackageName(), mCbStub,
+ return mSessionBinder.sendMediaButton(mContext.getPackageName(), mCbStub,
asSystemService, keyEvent);
} catch (RemoteException e) {
// System is dead. =(
@@ -187,8 +186,12 @@
break;
}
try {
- mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, true,
- direction, AudioManager.FLAG_SHOW_UI);
+ // Note: Need both package name and OP package name. Package name is used for
+ // RemoteUserInfo, and OP package name is used for AudioService's internal
+ // AppOpsManager usages.
+ mSessionBinder.adjustVolume(mContext.getPackageName(),
+ mContext.getOpPackageName(), mCbStub, true, direction,
+ AudioManager.FLAG_SHOW_UI);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling adjustVolumeBy", e);
}
@@ -198,8 +201,11 @@
final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE
| AudioManager.FLAG_FROM_KEY;
try {
- mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, true, 0,
- flags);
+ // Note: Need both package name and OP package name. Package name is used for
+ // RemoteUserInfo, and OP package name is used for AudioService's internal
+ // AppOpsManager usages.
+ mSessionBinder.adjustVolume(mContext.getPackageName(),
+ mContext.getOpPackageName(), mCbStub, true, 0, flags);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling adjustVolumeBy", e);
}
@@ -243,7 +249,10 @@
*/
public @Nullable List<MediaSession.QueueItem> getQueue() {
try {
- return mSessionBinder.getQueue();
+ ParceledListSlice queue = mSessionBinder.getQueue();
+ if (queue != null) {
+ return queue.getList();
+ }
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling getQueue.", e);
}
@@ -365,7 +374,11 @@
*/
public void setVolumeTo(int value, int flags) {
try {
- mSessionBinder.setVolumeTo(mContext.getOpPackageName(), mCbStub, value, flags);
+ // Note: Need both package name and OP package name. Package name is used for
+ // RemoteUserInfo, and OP package name is used for AudioService's internal
+ // AppOpsManager usages.
+ mSessionBinder.setVolumeTo(mContext.getPackageName(), mContext.getOpPackageName(),
+ mCbStub, value, flags);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling setVolumeTo.", e);
}
@@ -386,8 +399,11 @@
*/
public void adjustVolume(int direction, int flags) {
try {
- mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, false, direction,
- flags);
+ // Note: Need both package name and OP package name. Package name is used for
+ // RemoteUserInfo, and OP package name is used for AudioService's internal
+ // AppOpsManager usages.
+ mSessionBinder.adjustVolume(mContext.getPackageName(), mContext.getOpPackageName(),
+ mCbStub, false, direction, flags);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
}
@@ -453,7 +469,7 @@
throw new IllegalArgumentException("command cannot be null or empty");
}
try {
- mSessionBinder.sendCommand(mContext.getOpPackageName(), mCbStub, command, args, cb);
+ mSessionBinder.sendCommand(mContext.getPackageName(), mCbStub, command, args, cb);
} catch (RemoteException e) {
Log.d(TAG, "Dead object in sendCommand.", e);
}
@@ -519,7 +535,7 @@
if (!mCbRegistered) {
try {
- mSessionBinder.registerCallbackListener(mContext.getOpPackageName(), mCbStub);
+ mSessionBinder.registerCallbackListener(mContext.getPackageName(), mCbStub);
mCbRegistered = true;
} catch (RemoteException e) {
Log.e(TAG, "Dead object in registerCallback", e);
@@ -666,7 +682,7 @@
*/
public void prepare() {
try {
- mSessionBinder.prepare(mContext.getOpPackageName(), mCbStub);
+ mSessionBinder.prepare(mContext.getPackageName(), mCbStub);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling prepare.", e);
}
@@ -690,7 +706,7 @@
"You must specify a non-empty String for prepareFromMediaId.");
}
try {
- mSessionBinder.prepareFromMediaId(mContext.getOpPackageName(), mCbStub, mediaId,
+ mSessionBinder.prepareFromMediaId(mContext.getPackageName(), mCbStub, mediaId,
extras);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e);
@@ -717,7 +733,7 @@
query = "";
}
try {
- mSessionBinder.prepareFromSearch(mContext.getOpPackageName(), mCbStub, query,
+ mSessionBinder.prepareFromSearch(mContext.getPackageName(), mCbStub, query,
extras);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling prepare(" + query + ").", e);
@@ -742,7 +758,7 @@
"You must specify a non-empty Uri for prepareFromUri.");
}
try {
- mSessionBinder.prepareFromUri(mContext.getOpPackageName(), mCbStub, uri, extras);
+ mSessionBinder.prepareFromUri(mContext.getPackageName(), mCbStub, uri, extras);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling prepare(" + uri + ").", e);
}
@@ -753,7 +769,7 @@
*/
public void play() {
try {
- mSessionBinder.play(mContext.getOpPackageName(), mCbStub);
+ mSessionBinder.play(mContext.getPackageName(), mCbStub);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling play.", e);
}
@@ -772,7 +788,7 @@
"You must specify a non-empty String for playFromMediaId.");
}
try {
- mSessionBinder.playFromMediaId(mContext.getOpPackageName(), mCbStub, mediaId,
+ mSessionBinder.playFromMediaId(mContext.getPackageName(), mCbStub, mediaId,
extras);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
@@ -795,7 +811,7 @@
query = "";
}
try {
- mSessionBinder.playFromSearch(mContext.getOpPackageName(), mCbStub, query, extras);
+ mSessionBinder.playFromSearch(mContext.getPackageName(), mCbStub, query, extras);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling play(" + query + ").", e);
}
@@ -814,7 +830,7 @@
"You must specify a non-empty Uri for playFromUri.");
}
try {
- mSessionBinder.playFromUri(mContext.getOpPackageName(), mCbStub, uri, extras);
+ mSessionBinder.playFromUri(mContext.getPackageName(), mCbStub, uri, extras);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling play(" + uri + ").", e);
}
@@ -826,7 +842,7 @@
*/
public void skipToQueueItem(long id) {
try {
- mSessionBinder.skipToQueueItem(mContext.getOpPackageName(), mCbStub, id);
+ mSessionBinder.skipToQueueItem(mContext.getPackageName(), mCbStub, id);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
}
@@ -838,7 +854,7 @@
*/
public void pause() {
try {
- mSessionBinder.pause(mContext.getOpPackageName(), mCbStub);
+ mSessionBinder.pause(mContext.getPackageName(), mCbStub);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling pause.", e);
}
@@ -850,7 +866,7 @@
*/
public void stop() {
try {
- mSessionBinder.stop(mContext.getOpPackageName(), mCbStub);
+ mSessionBinder.stop(mContext.getPackageName(), mCbStub);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling stop.", e);
}
@@ -863,7 +879,7 @@
*/
public void seekTo(long pos) {
try {
- mSessionBinder.seekTo(mContext.getOpPackageName(), mCbStub, pos);
+ mSessionBinder.seekTo(mContext.getPackageName(), mCbStub, pos);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling seekTo.", e);
}
@@ -875,7 +891,7 @@
*/
public void fastForward() {
try {
- mSessionBinder.fastForward(mContext.getOpPackageName(), mCbStub);
+ mSessionBinder.fastForward(mContext.getPackageName(), mCbStub);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling fastForward.", e);
}
@@ -886,7 +902,7 @@
*/
public void skipToNext() {
try {
- mSessionBinder.next(mContext.getOpPackageName(), mCbStub);
+ mSessionBinder.next(mContext.getPackageName(), mCbStub);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling next.", e);
}
@@ -898,7 +914,7 @@
*/
public void rewind() {
try {
- mSessionBinder.rewind(mContext.getOpPackageName(), mCbStub);
+ mSessionBinder.rewind(mContext.getPackageName(), mCbStub);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling rewind.", e);
}
@@ -909,7 +925,7 @@
*/
public void skipToPrevious() {
try {
- mSessionBinder.previous(mContext.getOpPackageName(), mCbStub);
+ mSessionBinder.previous(mContext.getPackageName(), mCbStub);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling previous.", e);
}
@@ -924,7 +940,7 @@
*/
public void setRating(Rating rating) {
try {
- mSessionBinder.rate(mContext.getOpPackageName(), mCbStub, rating);
+ mSessionBinder.rate(mContext.getPackageName(), mCbStub, rating);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling rate.", e);
}
@@ -959,7 +975,7 @@
throw new IllegalArgumentException("CustomAction cannot be null.");
}
try {
- mSessionBinder.sendCustomAction(mContext.getOpPackageName(), mCbStub, action, args);
+ mSessionBinder.sendCustomAction(mContext.getPackageName(), mCbStub, action, args);
} catch (RemoteException e) {
Log.d(TAG, "Dead object in sendCustomAction.", e);
}
@@ -1056,10 +1072,10 @@
}
}
- private static final class CallbackStub extends ControllerCallbackLink.CallbackStub {
+ private final static class CallbackStub extends ISessionControllerCallback.Stub {
private final WeakReference<MediaController> mController;
- CallbackStub(MediaController controller) {
+ public CallbackStub(MediaController controller) {
mController = new WeakReference<MediaController>(controller);
}
@@ -1096,7 +1112,9 @@
}
@Override
- public void onQueueChanged(List<QueueItem> queue) {
+ public void onQueueChanged(ParceledListSlice parceledQueue) {
+ List<MediaSession.QueueItem> queue = parceledQueue == null ? null : parceledQueue
+ .getList();
MediaController controller = mController.get();
if (controller != null) {
controller.postMessage(MSG_UPDATE_QUEUE, queue, null);
@@ -1120,15 +1138,15 @@
}
@Override
- public void onVolumeInfoChanged(int volumeType, AudioAttributes attrs, int controlType,
- int maxVolume, int currentVolume) {
+ public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) {
MediaController controller = mController.get();
if (controller != null) {
- PlaybackInfo info = new PlaybackInfo(volumeType, attrs, controlType, maxVolume,
- currentVolume);
+ PlaybackInfo info = new PlaybackInfo(pvi.volumeType, pvi.audioAttrs,
+ pvi.controlType, pvi.maxVolume, pvi.currentVolume);
controller.postMessage(MSG_UPDATE_VOLUME, info, null);
}
}
+
}
private final static class MessageHandler extends Handler {
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 0332f4c..8962bb7 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -24,6 +24,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.MediaDescription;
import android.media.MediaMetadata;
@@ -129,7 +130,7 @@
private final MediaSession.Token mSessionToken;
private final MediaController mController;
private final ISession mBinder;
- private final SessionCallbackLink mCbStub;
+ private final CallbackStub mCbStub;
// Do not change the name of mCallback. Support lib accesses this by using reflection.
@UnsupportedAppUsage
@@ -172,7 +173,7 @@
}
mMaxBitmapSize = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize);
- mCbStub = new SessionCallbackLink(new CallbackStub(this));
+ mCbStub = new CallbackStub(this);
MediaSessionManager manager = (MediaSessionManager) context
.getSystemService(Context.MEDIA_SESSION_SERVICE);
try {
@@ -466,7 +467,7 @@
*/
public void setQueue(@Nullable List<QueueItem> queue) {
try {
- mBinder.setQueue(queue);
+ mBinder.setQueue(queue == null ? null : new ParceledListSlice<QueueItem>(queue));
} catch (RemoteException e) {
Log.wtf("Dead object in setQueue.", e);
}
@@ -1062,7 +1063,7 @@
/**
* @hide
*/
- public static final class CallbackStub extends SessionCallbackLink.CallbackStub {
+ public static class CallbackStub extends ISessionCallback.Stub {
private WeakReference<MediaSession> mMediaSession;
public CallbackStub(MediaSession session) {
@@ -1070,14 +1071,14 @@
}
private static RemoteUserInfo createRemoteUserInfo(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
+ ISessionControllerCallback caller) {
return new RemoteUserInfo(packageName, pid, uid,
- caller != null ? caller.getBinder() : null);
+ caller != null ? caller.asBinder() : null);
}
@Override
public void onCommand(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String command, Bundle args, ResultReceiver cb) {
+ ISessionControllerCallback caller, String command, Bundle args, ResultReceiver cb) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchCommand(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1103,7 +1104,7 @@
@Override
public void onMediaButtonFromController(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Intent mediaButtonIntent) {
+ ISessionControllerCallback caller, Intent mediaButtonIntent) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1113,7 +1114,7 @@
@Override
public void onPrepare(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
+ ISessionControllerCallback caller) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchPrepare(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1122,7 +1123,7 @@
@Override
public void onPrepareFromMediaId(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String mediaId,
+ ISessionControllerCallback caller, String mediaId,
Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -1133,7 +1134,7 @@
@Override
public void onPrepareFromSearch(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String query,
+ ISessionControllerCallback caller, String query,
Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -1144,7 +1145,7 @@
@Override
public void onPrepareFromUri(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Uri uri, Bundle extras) {
+ ISessionControllerCallback caller, Uri uri, Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchPrepareFromUri(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1154,7 +1155,7 @@
@Override
public void onPlay(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
+ ISessionControllerCallback caller) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchPlay(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1163,7 +1164,7 @@
@Override
public void onPlayFromMediaId(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String mediaId,
+ ISessionControllerCallback caller, String mediaId,
Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -1174,7 +1175,7 @@
@Override
public void onPlayFromSearch(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String query,
+ ISessionControllerCallback caller, String query,
Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -1185,7 +1186,7 @@
@Override
public void onPlayFromUri(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Uri uri, Bundle extras) {
+ ISessionControllerCallback caller, Uri uri, Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchPlayFromUri(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1195,7 +1196,7 @@
@Override
public void onSkipToTrack(String packageName, int pid, int uid,
- ControllerCallbackLink caller, long id) {
+ ISessionControllerCallback caller, long id) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchSkipToItem(createRemoteUserInfo(packageName, pid, uid, caller), id);
@@ -1204,7 +1205,7 @@
@Override
public void onPause(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
+ ISessionControllerCallback caller) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchPause(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1213,7 +1214,7 @@
@Override
public void onStop(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
+ ISessionControllerCallback caller) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchStop(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1222,7 +1223,7 @@
@Override
public void onNext(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
+ ISessionControllerCallback caller) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchNext(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1231,7 +1232,7 @@
@Override
public void onPrevious(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
+ ISessionControllerCallback caller) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchPrevious(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1240,7 +1241,7 @@
@Override
public void onFastForward(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
+ ISessionControllerCallback caller) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchFastForward(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1249,7 +1250,7 @@
@Override
public void onRewind(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
+ ISessionControllerCallback caller) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchRewind(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1258,7 +1259,7 @@
@Override
public void onSeekTo(String packageName, int pid, int uid,
- ControllerCallbackLink caller, long pos) {
+ ISessionControllerCallback caller, long pos) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchSeekTo(createRemoteUserInfo(packageName, pid, uid, caller), pos);
@@ -1266,7 +1267,7 @@
}
@Override
- public void onRate(String packageName, int pid, int uid, ControllerCallbackLink caller,
+ public void onRate(String packageName, int pid, int uid, ISessionControllerCallback caller,
Rating rating) {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -1276,7 +1277,7 @@
@Override
public void onCustomAction(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String action, Bundle args) {
+ ISessionControllerCallback caller, String action, Bundle args) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchCustomAction(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1286,7 +1287,7 @@
@Override
public void onAdjustVolume(String packageName, int pid, int uid,
- ControllerCallbackLink caller, int direction) {
+ ISessionControllerCallback caller, int direction) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchAdjustVolume(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1296,7 +1297,7 @@
@Override
public void onSetVolumeTo(String packageName, int pid, int uid,
- ControllerCallbackLink caller, int value) {
+ ISessionControllerCallback caller, int value) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchSetVolumeTo(createRemoteUserInfo(packageName, pid, uid, caller),
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 73dd55c..4221d66 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -96,7 +96,7 @@
* @return The binder object from the system
* @hide
*/
- public @NonNull ISession createSession(@NonNull SessionCallbackLink cbStub,
+ public @NonNull ISession createSession(@NonNull MediaSession.CallbackStub cbStub,
@NonNull String tag, int userId) throws RemoteException {
return mService.createSession(mContext.getPackageName(), cbStub, tag, userId);
}
@@ -312,7 +312,7 @@
private void dispatchMediaKeyEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent,
boolean needWakeLock) {
try {
- mService.dispatchMediaKeyEvent(mContext.getOpPackageName(), asSystemService, keyEvent,
+ mService.dispatchMediaKeyEvent(mContext.getPackageName(), asSystemService, keyEvent,
needWakeLock);
} catch (RemoteException e) {
Log.e(TAG, "Failed to send key event.", e);
@@ -348,8 +348,8 @@
private void dispatchVolumeKeyEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent,
int stream, boolean musicOnly) {
try {
- mService.dispatchVolumeKeyEvent(mContext.getOpPackageName(), asSystemService, keyEvent,
- stream, musicOnly);
+ mService.dispatchVolumeKeyEvent(mContext.getPackageName(), mContext.getOpPackageName(),
+ asSystemService, keyEvent, stream, musicOnly);
} catch (RemoteException e) {
Log.e(TAG, "Failed to send volume key event.", e);
}
@@ -369,8 +369,8 @@
*/
public void dispatchAdjustVolume(int suggestedStream, int direction, int flags) {
try {
- mService.dispatchAdjustVolume(mContext.getOpPackageName(), suggestedStream, direction,
- flags);
+ mService.dispatchAdjustVolume(mContext.getPackageName(), mContext.getOpPackageName(),
+ suggestedStream, direction, flags);
} catch (RemoteException e) {
Log.e(TAG, "Failed to send adjust volume.", e);
}
diff --git a/media/java/android/media/session/SessionCallbackLink.java b/media/java/android/media/session/SessionCallbackLink.java
deleted file mode 100644
index b13afb5..0000000
--- a/media/java/android/media/session/SessionCallbackLink.java
+++ /dev/null
@@ -1,572 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.session;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.content.Intent;
-import android.media.Rating;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-
-/**
- * Handles incoming commands to {@link MediaSession.Callback}.
- * @hide
- */
-@SystemApi
-public final class SessionCallbackLink implements Parcelable {
- CallbackStub mCallbackStub;
- ISessionCallback mISessionCallback;
-
- /**
- * Creator for stub (Callee)
- */
- public SessionCallbackLink(@NonNull CallbackStub callbackStub) {
- mCallbackStub = callbackStub;
- mISessionCallback = new CallbackStubProxy();
- }
-
- /**
- * Creator for interface (Caller)
- */
- SessionCallbackLink(Parcel in) {
- mCallbackStub = null;
- mISessionCallback = ISessionCallback.Stub.asInterface(in.readStrongBinder());
- }
-
- /** Interface method for ISessionCallback.notifyCommand */
- public void notifyCommand(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String command, Bundle args, ResultReceiver cb) {
- try {
- mISessionCallback.notifyCommand(packageName, pid, uid, caller, command, args, cb);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyMediaButton */
- public void notifyMediaButton(String packageName, int pid, int uid,
- Intent mediaButtonIntent, int sequenceNumber, ResultReceiver cb) {
- try {
- mISessionCallback.notifyMediaButton(packageName, pid, uid, mediaButtonIntent,
- sequenceNumber, cb);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyMediaButtonFromController */
- public void notifyMediaButtonFromController(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Intent mediaButtonIntent) {
- try {
- mISessionCallback.notifyMediaButtonFromController(packageName, pid, uid, caller,
- mediaButtonIntent);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyPrepare */
- public void notifyPrepare(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- try {
- mISessionCallback.notifyPrepare(packageName, pid, uid, caller);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyPrepareFromMediaId */
- public void notifyPrepareFromMediaId(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String mediaId, Bundle extras) {
- try {
- mISessionCallback.notifyPrepareFromMediaId(packageName, pid, uid, caller, mediaId,
- extras);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyPrepareFromSearch */
- public void notifyPrepareFromSearch(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String query, Bundle extras) {
- try {
- mISessionCallback.notifyPrepareFromSearch(packageName, pid, uid, caller, query, extras);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyPrepareFromUri */
- public void notifyPrepareFromUri(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Uri uri, Bundle extras) {
- try {
- mISessionCallback.notifyPrepareFromUri(packageName, pid, uid, caller, uri, extras);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyPlay */
- public void notifyPlay(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- try {
- mISessionCallback.notifyPlay(packageName, pid, uid, caller);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyPlayFromMediaId */
- public void notifyPlayFromMediaId(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String mediaId, Bundle extras) {
- try {
- mISessionCallback.notifyPlayFromMediaId(packageName, pid, uid, caller, mediaId, extras);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyPlayFromSearch */
- public void notifyPlayFromSearch(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String query, Bundle extras) {
- try {
- mISessionCallback.notifyPlayFromSearch(packageName, pid, uid, caller, query, extras);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyPlayFromUri */
- public void notifyPlayFromUri(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Uri uri, Bundle extras) {
- try {
- mISessionCallback.notifyPlayFromUri(packageName, pid, uid, caller, uri, extras);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifySkipToTrack */
- public void notifySkipToTrack(String packageName, int pid, int uid,
- ControllerCallbackLink caller, long id) {
- try {
- mISessionCallback.notifySkipToTrack(packageName, pid, uid, caller, id);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyPause */
- public void notifyPause(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- try {
- mISessionCallback.notifyPause(packageName, pid, uid, caller);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyStop */
- public void notifyStop(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- try {
- mISessionCallback.notifyStop(packageName, pid, uid, caller);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyNext */
- public void notifyNext(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- try {
- mISessionCallback.notifyNext(packageName, pid, uid, caller);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyPrevious */
- public void notifyPrevious(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- try {
- mISessionCallback.notifyPrevious(packageName, pid, uid, caller);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyFastForward */
- public void notifyFastForward(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- try {
- mISessionCallback.notifyFastForward(packageName, pid, uid, caller);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyRewind */
- public void notifyRewind(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- try {
- mISessionCallback.notifyRewind(packageName, pid, uid, caller);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifySeekTo */
- public void notifySeekTo(String packageName, int pid, int uid,
- ControllerCallbackLink caller, long pos) {
- try {
- mISessionCallback.notifySeekTo(packageName, pid, uid, caller, pos);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyRate */
- public void notifyRate(String packageName, int pid, int uid, ControllerCallbackLink caller,
- Rating rating) {
- try {
- mISessionCallback.notifyRate(packageName, pid, uid, caller, rating);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyCustomAction */
- public void notifyCustomAction(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String action, Bundle args) {
- try {
- mISessionCallback.notifyCustomAction(packageName, pid, uid, caller, action, args);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifyAdjustVolume */
- public void notifyAdjustVolume(String packageName, int pid, int uid,
- ControllerCallbackLink caller, int direction) {
- try {
- mISessionCallback.notifyAdjustVolume(packageName, pid, uid, caller, direction);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Interface method for ISessionCallback.notifySetVolumeTo */
- public void notifySetVolumeTo(String packageName, int pid, int uid,
- ControllerCallbackLink caller, int value) {
- try {
- mISessionCallback.notifySetVolumeTo(packageName, pid, uid, caller, value);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Gets the binder */
- public IBinder getBinder() {
- return mISessionCallback.asBinder();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeStrongBinder(mISessionCallback.asBinder());
- }
-
- public static final Parcelable.Creator<SessionCallbackLink> CREATOR =
- new Parcelable.Creator<SessionCallbackLink>() {
- @Override
- public SessionCallbackLink createFromParcel(Parcel in) {
- return new SessionCallbackLink(in);
- }
-
- @Override
- public SessionCallbackLink[] newArray(int size) {
- return new SessionCallbackLink[size];
- }
- };
-
- /**
- * Class for Stub implementation
- */
- public abstract static class CallbackStub {
- /** Stub method for ISessionCallback.notifyCommand */
- public void onCommand(String packageName, int pid, int uid, ControllerCallbackLink caller,
- String command, Bundle args, ResultReceiver cb) {
- }
-
- /** Stub method for ISessionCallback.notifyMediaButton */
- public void onMediaButton(String packageName, int pid, int uid, Intent mediaButtonIntent,
- int sequenceNumber, ResultReceiver cb) {
- }
-
- /** Stub method for ISessionCallback.notifyMediaButtonFromController */
- public void onMediaButtonFromController(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Intent mediaButtonIntent) {
- }
-
- /** Stub method for ISessionCallback.notifyPrepare */
- public void onPrepare(String packageName, int pid, int uid, ControllerCallbackLink caller) {
- }
-
- /** Stub method for ISessionCallback.notifyPrepareFromMediaId */
- public void onPrepareFromMediaId(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String mediaId, Bundle extras) {
- }
-
- /** Stub method for ISessionCallback.notifyPrepareFromSearch */
- public void onPrepareFromSearch(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String query, Bundle extras) {
- }
-
- /** Stub method for ISessionCallback.notifyPrepareFromUri */
- public void onPrepareFromUri(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Uri uri, Bundle extras) {
- }
-
- /** Stub method for ISessionCallback.notifyPlay */
- public void onPlay(String packageName, int pid, int uid, ControllerCallbackLink caller) {
- }
-
- /** Stub method for ISessionCallback.notifyPlayFromMediaId */
- public void onPlayFromMediaId(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String mediaId, Bundle extras) {
- }
-
- /** Stub method for ISessionCallback.notifyPlayFromSearch */
- public void onPlayFromSearch(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String query, Bundle extras) {
- }
-
- /** Stub method for ISessionCallback.notifyPlayFromUri */
- public void onPlayFromUri(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Uri uri, Bundle extras) {
- }
-
- /** Stub method for ISessionCallback.notifySkipToTrack */
- public void onSkipToTrack(String packageName, int pid, int uid,
- ControllerCallbackLink caller, long id) {
- }
-
- /** Stub method for ISessionCallback.notifyPause */
- public void onPause(String packageName, int pid, int uid, ControllerCallbackLink caller) {
- }
-
- /** Stub method for ISessionCallback.notifyStop */
- public void onStop(String packageName, int pid, int uid, ControllerCallbackLink caller) {
- }
-
- /** Stub method for ISessionCallback.notifyNext */
- public void onNext(String packageName, int pid, int uid, ControllerCallbackLink caller) {
- }
-
- /** Stub method for ISessionCallback.notifyPrevious */
- public void onPrevious(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- }
-
- /** Stub method for ISessionCallback.notifyFastForward */
- public void onFastForward(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- }
-
- /** Stub method for ISessionCallback.notifyRewind */
- public void onRewind(String packageName, int pid, int uid, ControllerCallbackLink caller) {
- }
-
- /** Stub method for ISessionCallback.notifySeekTo */
- public void onSeekTo(String packageName, int pid, int uid, ControllerCallbackLink caller,
- long pos) {
- }
-
- /** Stub method for ISessionCallback.notifyRate */
- public void onRate(String packageName, int pid, int uid, ControllerCallbackLink caller,
- Rating rating) {
- }
-
- /** Stub method for ISessionCallback.notifyCustomAction */
- public void onCustomAction(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String action, Bundle args) {
- }
-
- /** Stub method for ISessionCallback.notifyAdjustVolume */
- public void onAdjustVolume(String packageName, int pid, int uid,
- ControllerCallbackLink caller, int direction) {
- }
-
- /** Stub method for ISessionCallback.notifySetVolumeTo */
- public void onSetVolumeTo(String packageName, int pid, int uid,
- ControllerCallbackLink caller, int value) {
- }
- }
-
- private class CallbackStubProxy extends ISessionCallback.Stub {
- @Override
- public void notifyCommand(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String command, Bundle args, ResultReceiver cb) {
- mCallbackStub.onCommand(packageName, pid, uid, caller, command, args, cb);
- }
-
- @Override
- public void notifyMediaButton(String packageName, int pid, int uid,
- Intent mediaButtonIntent, int sequenceNumber, ResultReceiver cb) {
- mCallbackStub.onMediaButton(packageName, pid, uid, mediaButtonIntent, sequenceNumber,
- cb);
- }
-
- @Override
- public void notifyMediaButtonFromController(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Intent mediaButtonIntent) {
- mCallbackStub.onMediaButtonFromController(packageName, pid, uid, caller,
- mediaButtonIntent);
- }
-
- @Override
- public void notifyPrepare(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- mCallbackStub.onPrepare(packageName, pid, uid, caller);
- }
-
- @Override
- public void notifyPrepareFromMediaId(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String mediaId, Bundle extras) {
- mCallbackStub.onPrepareFromMediaId(packageName, pid, uid, caller, mediaId, extras);
- }
-
- @Override
- public void notifyPrepareFromSearch(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String query, Bundle extras) {
- mCallbackStub.onPrepareFromSearch(packageName, pid, uid, caller, query, extras);
- }
-
- @Override
- public void notifyPrepareFromUri(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Uri uri, Bundle extras) {
- mCallbackStub.onPrepareFromUri(packageName, pid, uid, caller, uri, extras);
- }
-
- @Override
- public void notifyPlay(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- mCallbackStub.onPlay(packageName, pid, uid, caller);
- }
-
- @Override
- public void notifyPlayFromMediaId(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String mediaId, Bundle extras) {
- mCallbackStub.onPlayFromMediaId(packageName, pid, uid, caller, mediaId, extras);
- }
-
- @Override
- public void notifyPlayFromSearch(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String query, Bundle extras) {
- mCallbackStub.onPlayFromSearch(packageName, pid, uid, caller, query, extras);
- }
-
- @Override
- public void notifyPlayFromUri(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Uri uri, Bundle extras) {
- mCallbackStub.onPlayFromUri(packageName, pid, uid, caller, uri, extras);
- }
-
- @Override
- public void notifySkipToTrack(String packageName, int pid, int uid,
- ControllerCallbackLink caller, long id) {
- mCallbackStub.onSkipToTrack(packageName, pid, uid, caller, id);
- }
-
- @Override
- public void notifyPause(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- mCallbackStub.onPause(packageName, pid, uid, caller);
- }
-
- @Override
- public void notifyStop(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- mCallbackStub.onStop(packageName, pid, uid, caller);
- }
-
- @Override
- public void notifyNext(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- mCallbackStub.onNext(packageName, pid, uid, caller);
- }
-
- @Override
- public void notifyPrevious(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- mCallbackStub.onPrevious(packageName, pid, uid, caller);
- }
-
- @Override
- public void notifyFastForward(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- mCallbackStub.onFastForward(packageName, pid, uid, caller);
- }
-
- @Override
- public void notifyRewind(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- mCallbackStub.onRewind(packageName, pid, uid, caller);
- }
-
- @Override
- public void notifySeekTo(String packageName, int pid, int uid,
- ControllerCallbackLink caller, long pos) {
- mCallbackStub.onSeekTo(packageName, pid, uid, caller, pos);
- }
-
- @Override
- public void notifyRate(String packageName, int pid, int uid, ControllerCallbackLink caller,
- Rating rating) {
- mCallbackStub.onRate(packageName, pid, uid, caller, rating);
- }
-
- @Override
- public void notifyCustomAction(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String action, Bundle args) {
- mCallbackStub.onCustomAction(packageName, pid, uid, caller, action, args);
- }
-
- @Override
- public void notifyAdjustVolume(String packageName, int pid, int uid,
- ControllerCallbackLink caller, int direction) {
- mCallbackStub.onAdjustVolume(packageName, pid, uid, caller, direction);
- }
-
- @Override
- public void notifySetVolumeTo(String packageName, int pid, int uid,
- ControllerCallbackLink caller, int value) {
- mCallbackStub.onSetVolumeTo(packageName, pid, uid, caller, value);
- }
- }
-}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 7481fff..f75f69b 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -95,14 +95,16 @@
],
shared_libs: [
- "android.hardware.cas@1.0", // for CasManager. VNDK???
- "android.hardware.cas.native@1.0", // CasManager. VNDK???
+ // MediaCas
+ "android.hardware.cas@1.0",
+ "android.hardware.cas.native@1.0",
"android.hidl.allocator@1.0",
+ "libhidlbase",
"libhidlmemory",
- "libbinder",
- "libgui", // for VideoFrameScheduler
- "libhidlbase", // VNDK???
- "libpowermanager", // for JWakeLock. to be removed
+
+ "libpowermanager", // Used by JWakeLock. Will be replace with public SDJ API.
+ "libmediametrics", // Used by MediaMetrics. Will be replaced with stable C API.
+ "libbinder", // Used by JWakeLock and MediaMetrics.
"libutils", // Have to use shared lib to make libandroid_runtime behave correctly.
// Otherwise, AndroidRuntime::getJNIEnv() will return NULL.
@@ -124,7 +126,6 @@
"libmedia_helper",
"libmedia_player2_util",
"libmediaextractor",
- "libmediametrics",
"libmediaplayer2",
"libmediaplayer2-protos",
"libmediandk_utils",
diff --git a/native/webview/plat_support/Android.bp b/native/webview/plat_support/Android.bp
index 96c9c1c..0936256 100644
--- a/native/webview/plat_support/Android.bp
+++ b/native/webview/plat_support/Android.bp
@@ -22,6 +22,7 @@
name: "libwebviewchromium_plat_support",
srcs: [
+ "draw_functor.cpp",
"draw_gl_functor.cpp",
"draw_vk_functor.cpp",
"functor_utils.cpp",
diff --git a/native/webview/plat_support/draw_fn.h b/native/webview/plat_support/draw_fn.h
index 8d48a58..6afd883 100644
--- a/native/webview/plat_support/draw_fn.h
+++ b/native/webview/plat_support/draw_fn.h
@@ -129,31 +129,31 @@
// Called on render thread while UI thread is blocked. Called for both GL and
// VK.
-typedef void AwDrawFn_OnSync(int functor, AwDrawFn_OnSyncParams* params);
+typedef void AwDrawFn_OnSync(int functor, void* data, AwDrawFn_OnSyncParams* params);
// Called on render thread when either the context is destroyed _or_ when the
// functor's last reference goes away. Will always be called with an active
// context. Called for both GL and VK.
-typedef void AwDrawFn_OnContextDestroyed(int functor);
+typedef void AwDrawFn_OnContextDestroyed(int functor, void* data);
// Called on render thread when the last reference to the handle goes away and
-// the handle is considered irrevocably destroyed. Will always be proceeded by
+// the handle is considered irrevocably destroyed. Will always be preceded by
// a call to OnContextDestroyed if this functor had ever been drawn. Called for
// both GL and VK.
-typedef void AwDrawFn_OnDestroyed(int functor);
+typedef void AwDrawFn_OnDestroyed(int functor, void* data);
// Only called for GL.
-typedef void AwDrawFn_DrawGL(int functor, AwDrawFn_DrawGLParams* params);
+typedef void AwDrawFn_DrawGL(int functor, void* data, AwDrawFn_DrawGLParams* params);
// Initialize vulkan state. Needs to be called again after any
// OnContextDestroyed. Only called for Vulkan.
-typedef void AwDrawFn_InitVk(int functor, AwDrawFn_InitVkParams* params);
+typedef void AwDrawFn_InitVk(int functor, void* data, AwDrawFn_InitVkParams* params);
// Only called for Vulkan.
-typedef void AwDrawFn_DrawVk(int functor, AwDrawFn_DrawVkParams* params);
+typedef void AwDrawFn_DrawVk(int functor, void* data, AwDrawFn_DrawVkParams* params);
// Only called for Vulkan.
-typedef void AwDrawFn_PostDrawVk(int functor,
+typedef void AwDrawFn_PostDrawVk(int functor, void* data,
AwDrawFn_PostDrawVkParams* params);
struct AwDrawFnFunctorCallbacks {
@@ -176,7 +176,7 @@
typedef AwDrawFnRenderMode AwDrawFn_QueryRenderMode(void);
// Create a functor. |functor_callbacks| should be valid until OnDestroyed.
-typedef int AwDrawFn_CreateFunctor(AwDrawFnFunctorCallbacks* functor_callbacks);
+typedef int AwDrawFn_CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks);
// May be called on any thread to signal that the functor should be destroyed.
// The functor will receive an onDestroyed when the last usage of it is
diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp
new file mode 100644
index 0000000..820bac5
--- /dev/null
+++ b/native/webview/plat_support/draw_functor.cpp
@@ -0,0 +1,144 @@
+/*
+ * 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.
+ */
+
+#include "draw_fn.h"
+
+#include <jni.h>
+#include <private/hwui/WebViewFunctor.h>
+#include <utils/Log.h>
+
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+#define COMPILE_ASSERT(expr, err) \
+__unused static const char (err)[(expr) ? 1 : -1] = "";
+
+namespace android {
+namespace {
+
+struct SupportData {
+ void* const data;
+ AwDrawFnFunctorCallbacks callbacks;
+};
+
+void onSync(int functor, void* data,
+ const uirenderer::WebViewSyncData& syncData) {
+ AwDrawFn_OnSyncParams params = {
+ .version = kAwDrawFnVersion,
+ .apply_force_dark = syncData.applyForceDark,
+ };
+ SupportData* support = static_cast<SupportData*>(data);
+ support->callbacks.on_sync(functor, support->data, ¶ms);
+}
+
+void onContextDestroyed(int functor, void* data) {
+ SupportData* support = static_cast<SupportData*>(data);
+ support->callbacks.on_context_destroyed(functor, support->data);
+}
+
+void onDestroyed(int functor, void* data) {
+ SupportData* support = static_cast<SupportData*>(data);
+ support->callbacks.on_destroyed(functor, support->data);
+ delete support;
+}
+
+void draw_gl(int functor, void* data,
+ const uirenderer::DrawGlInfo& draw_gl_params) {
+ AwDrawFn_DrawGLParams params = {
+ .version = kAwDrawFnVersion,
+ .clip_left = draw_gl_params.clipLeft,
+ .clip_top = draw_gl_params.clipTop,
+ .clip_right = draw_gl_params.clipRight,
+ .clip_bottom = draw_gl_params.clipBottom,
+ .width = draw_gl_params.width,
+ .height = draw_gl_params.height,
+ .is_layer = draw_gl_params.isLayer,
+ };
+ COMPILE_ASSERT(NELEM(params.transform) == NELEM(draw_gl_params.transform),
+ mismatched_transform_matrix_sizes);
+ for (int i = 0; i < NELEM(params.transform); ++i) {
+ params.transform[i] = draw_gl_params.transform[i];
+ }
+ SupportData* support = static_cast<SupportData*>(data);
+ support->callbacks.draw_gl(functor, support->data, ¶ms);
+}
+
+int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) {
+ static bool callbacks_initialized = false;
+ static uirenderer::WebViewFunctorCallbacks webview_functor_callbacks = {
+ .onSync = &onSync,
+ .onContextDestroyed = &onContextDestroyed,
+ .onDestroyed = &onDestroyed,
+ };
+ if (!callbacks_initialized) {
+ switch (uirenderer::WebViewFunctor_queryPlatformRenderMode()) {
+ case uirenderer::RenderMode::OpenGL_ES:
+ webview_functor_callbacks.gles.draw = &draw_gl;
+ break;
+ case uirenderer::RenderMode::Vulkan:
+ break;
+ }
+ callbacks_initialized = true;
+ }
+ SupportData* support = new SupportData{
+ .data = data,
+ .callbacks = *functor_callbacks,
+ };
+ int functor = uirenderer::WebViewFunctor_create(
+ support, webview_functor_callbacks,
+ uirenderer::WebViewFunctor_queryPlatformRenderMode());
+ if (functor <= 0) delete support;
+ return functor;
+}
+
+void ReleaseFunctor(int functor) {
+ uirenderer::WebViewFunctor_release(functor);
+}
+
+AwDrawFnRenderMode QueryRenderMode(void) {
+ switch (uirenderer::WebViewFunctor_queryPlatformRenderMode()) {
+ case uirenderer::RenderMode::OpenGL_ES:
+ return AW_DRAW_FN_RENDER_MODE_OPENGL_ES;
+ case uirenderer::RenderMode::Vulkan:
+ return AW_DRAW_FN_RENDER_MODE_VULKAN;
+ }
+}
+
+jlong GetDrawFnFunctionTable() {
+ static AwDrawFnFunctionTable function_table = {
+ .version = kAwDrawFnVersion,
+ .query_render_mode = &QueryRenderMode,
+ .create_functor = &CreateFunctor,
+ .release_functor = &ReleaseFunctor,
+ };
+ return reinterpret_cast<intptr_t>(&function_table);
+}
+
+const char kClassName[] = "com/android/webview/chromium/DrawFunctor";
+const JNINativeMethod kJniMethods[] = {
+ {"nativeGetFunctionTable", "()J",
+ reinterpret_cast<void*>(GetDrawFnFunctionTable)},
+};
+
+} // namespace
+
+void RegisterDrawFunctor(JNIEnv* env) {
+ jclass clazz = env->FindClass(kClassName);
+ LOG_ALWAYS_FATAL_IF(!clazz, "Unable to find class '%s'", kClassName);
+
+ int res = env->RegisterNatives(clazz, kJniMethods, NELEM(kJniMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "register native methods failed: res=%d", res);
+}
+
+} // namespace android
diff --git a/native/webview/plat_support/jni_entry_point.cpp b/native/webview/plat_support/jni_entry_point.cpp
index 4771be1..9599fa6 100644
--- a/native/webview/plat_support/jni_entry_point.cpp
+++ b/native/webview/plat_support/jni_entry_point.cpp
@@ -21,6 +21,7 @@
namespace android {
+void RegisterDrawFunctor(JNIEnv* env);
void RegisterDrawGLFunctor(JNIEnv* env);
void RegisterGraphicsUtils(JNIEnv* env);
@@ -30,6 +31,7 @@
JNIEnv* env = NULL;
jint ret = vm->AttachCurrentThread(&env, NULL);
LOG_ALWAYS_FATAL_IF(ret != JNI_OK, "AttachCurrentThread failed");
+ android::RegisterDrawFunctor(env);
android::RegisterDrawGLFunctor(env);
android::RegisterGraphicsUtils(env);
diff --git a/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
index d5dd3c3..4ef926f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
+++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
@@ -28,7 +28,7 @@
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher;
/**
* A view that forms the header of the notification panel. This view will ensure that any
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 7028999c..dbddf71 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -280,7 +280,9 @@
buildNavBarContent();
attachNavBarWindows();
- mNavigationBarController.createNavigationBars();
+ // There has been a car customized nav bar on the default display, so just create nav bars
+ // on external displays.
+ mNavigationBarController.createNavigationBars(false /* includeDefaultDisplay */);
}
private void buildNavBarContent() {
@@ -447,12 +449,6 @@
}
}
-
- @Override
- public View getNavigationBarWindow() {
- return mNavigationBarWindow;
- }
-
@Override
protected View.OnTouchListener getStatusBarWindowTouchListener() {
// Usually, a touch on the background window will dismiss the notification shade. However,
diff --git a/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml
index 1a4d7b7..b063e13 100644
--- a/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml
+++ b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml
@@ -33,33 +33,49 @@
android:textAppearance="@style/BarChart.Text.HeaderTitle"/>
<LinearLayout
+ android:id="@+id/bar_views_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="center|bottom">
+ android:gravity="center"
+ android:orientation="vertical">
- <com.android.settingslib.widget.BarView
- android:id="@+id/bar_view1"
- style="@style/BarViewStyle"
- settings:barColor="#FA7B17"/>
- <com.android.settingslib.widget.BarView
- android:id="@+id/bar_view2"
- style="@style/BarViewStyle"
- settings:barColor="#F439A0"/>
- <com.android.settingslib.widget.BarView
- android:id="@+id/bar_view3"
- style="@style/BarViewStyle"
- settings:barColor="#A142F4"/>
- <com.android.settingslib.widget.BarView
- android:id="@+id/bar_view4"
- style="@style/BarViewStyle"
- settings:barColor="#24C1E0"/>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center|bottom">
+
+ <com.android.settingslib.widget.BarView
+ android:id="@+id/bar_view1"
+ style="@style/BarViewStyle"
+ settings:barColor="#FA7B17"/>
+ <com.android.settingslib.widget.BarView
+ android:id="@+id/bar_view2"
+ style="@style/BarViewStyle"
+ settings:barColor="#F439A0"/>
+ <com.android.settingslib.widget.BarView
+ android:id="@+id/bar_view3"
+ style="@style/BarViewStyle"
+ settings:barColor="#A142F4"/>
+ <com.android.settingslib.widget.BarView
+ android:id="@+id/bar_view4"
+ style="@style/BarViewStyle"
+ settings:barColor="#24C1E0"/>
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/bar_chart_details"
+ style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:gravity="center"/>
</LinearLayout>
- <Button
- android:id="@+id/bar_chart_details"
- style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:gravity="center"/>
+ <TextView
+ android:id="@+id/empty_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/settings_bar_view_max_height"
+ android:gravity="center"
+ android:visibility="gone"
+ android:textAppearance="@style/BarChart.Text.Summary"/>
</LinearLayout>
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartInfo.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartInfo.java
new file mode 100644
index 0000000..eeaf273
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartInfo.java
@@ -0,0 +1,176 @@
+/*
+ * 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.settingslib.widget;
+
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * BarChartInfo is responsible for storing information about {@link BarChartPreference}.
+ */
+public class BarChartInfo {
+ @StringRes
+ private final int mTitle;
+ @StringRes
+ private final int mDetails;
+ @StringRes
+ private final int mEmptyText;
+ private final View.OnClickListener mDetailsOnClickListener;
+
+ private BarViewInfo[] mBarViewInfos;
+
+ /**
+ * Gets the resource id for the title shown in {@link BarChartPreference}.
+ *
+ * @return the string resource id for title.
+ */
+ public int getTitle() {
+ return mTitle;
+ }
+
+ /**
+ * Gets the resource id for the details shown in {@link BarChartPreference}.
+ *
+ * @return the string resource id for details.
+ */
+ public int getDetails() {
+ return mDetails;
+ }
+
+ /**
+ * Gets the resource id for the empty text shown in {@link BarChartPreference} when there is no
+ * any bar view in {@link BarChartPreference}.
+ *
+ * @return the string resource id for empty text.
+ */
+ public int getEmptyText() {
+ return mEmptyText;
+ }
+
+ /**
+ * Gets the click listener for the details view.
+ *
+ * @return click listener for details view.
+ */
+ public View.OnClickListener getDetailsOnClickListener() {
+ return mDetailsOnClickListener;
+ }
+
+ /**
+ * Gets an array which contains up to four {@link BarViewInfo}
+ *
+ * @return an array holding the current all {@link BarViewInfo} state of the bar chart.
+ */
+ public BarViewInfo[] getBarViewInfos() {
+ return mBarViewInfos;
+ }
+
+ void setBarViewInfos(BarViewInfo[] barViewInfos) {
+ mBarViewInfos = barViewInfos;
+ }
+
+ private BarChartInfo(Builder builder) {
+ mTitle = builder.mTitle;
+ mDetails = builder.mDetails;
+ mEmptyText = builder.mEmptyText;
+ mDetailsOnClickListener = builder.mDetailsOnClickListener;
+
+ if (builder.mBarViewInfos != null) {
+ mBarViewInfos = builder.mBarViewInfos.stream().toArray(BarViewInfo[]::new);
+ }
+ }
+
+ /**
+ * Builder class for {@link BarChartInfo}
+ */
+ public static class Builder {
+ @StringRes
+ private int mTitle;
+ @StringRes
+ private int mDetails;
+ @StringRes
+ private int mEmptyText;
+ private View.OnClickListener mDetailsOnClickListener;
+ private List<BarViewInfo> mBarViewInfos;
+
+ /**
+ * Creates an instance of a {@link BarChartInfo} based on the current builder settings.
+ *
+ * @return The {@link BarChartInfo}.
+ */
+ public BarChartInfo build() {
+ if (mTitle == 0) {
+ throw new IllegalStateException("You must call Builder#setTitle() once.");
+ }
+ return new BarChartInfo(this);
+ }
+
+ /**
+ * Sets the string resource id for the title.
+ */
+ public Builder setTitle(@StringRes int title) {
+ mTitle = title;
+ return this;
+ }
+
+ /**
+ * Sets the string resource id for the details.
+ */
+ public Builder setDetails(@StringRes int details) {
+ mDetails = details;
+ return this;
+ }
+
+ /**
+ * Sets the string resource id for the empty text.
+ */
+ public Builder setEmptyText(@StringRes int emptyText) {
+ mEmptyText = emptyText;
+ return this;
+ }
+
+ /**
+ * Sets the click listener for details view.
+ */
+ public Builder setDetailsOnClickListener(
+ @Nullable View.OnClickListener clickListener) {
+ mDetailsOnClickListener = clickListener;
+ return this;
+ }
+
+ /**
+ * Adds a {@link BarViewInfo} for {@link BarChartPreference}.
+ * Maximum of 4 {@link BarViewInfo} can be added.
+ */
+ public Builder addBarViewInfo(@NonNull BarViewInfo barViewInfo) {
+ if (mBarViewInfos == null) {
+ mBarViewInfos = new ArrayList<>();
+ }
+ if (mBarViewInfos.size() >= BarChartPreference.MAXIMUM_BAR_VIEWS) {
+ throw new IllegalStateException("We only support up to four bar views");
+ }
+ mBarViewInfos.add(barViewInfo);
+ return this;
+ }
+ }
+}
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
index 0a4b24c..eed66e9 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
@@ -1,5 +1,5 @@
/*
- * 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.
@@ -24,7 +24,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
@@ -41,31 +40,45 @@
* android:key="bar_chart"/>
* </pre>
*
- * <p>This code sample demonstrates how to initialize the contents of the BarChartPreference defined
- * in the previous XML layout:
- *
- * <pre>
- * BarViewInfo[] viewsInfo = new BarViewInfo [] {
- * new BarViewInfo(icon, 18, res of summary),
- * new BarViewInfo(icon, 25, res of summary),
- * new BarViewInfo(icon, 10, res of summary),
- * new BarViewInfo(icon, 3, res of summary),
- * };
- * </pre>
+ * <p>This code sample demonstrates how to initialize the contents of the BarChartPreference
+ * defined in the previous XML layout:
*
* <pre>
* BarChartPreference preference = ((BarChartPreference) findPreference("bar_chart"));
*
- * preference.setBarChartTitleRes(R.string.title_res);
- * preference.setBarChartDetailsRes(R.string.details_res);
- * preference.setBarChartDetailsClickListener(v -> doSomething());
- * preference.setAllBarViewsData(viewsInfo);
+ * BarChartInfo info = new BarChartInfo.Builder()
+ * .setTitle(R.string.permission_bar_chart_title)
+ * .setDetails(R.string.permission_bar_chart_details)
+ * .setEmptyText(R.string.permission_bar_chart_empty_text)
+ * .addBarViewInfo(new barViewInfo(...))
+ * .addBarViewInfo(new barViewInfo(...))
+ * .addBarViewInfo(new barViewInfo(...))
+ * .addBarViewInfo(new barViewInfo(...))
+ * .setDetailsOnClickListener(v -> doSomething())
+ * .build();
+ *
+ * preference.initializeBarChart(info);
+ * </pre>
+ *
+ *
+ * <p>You also can update new information for bar views by
+ * {@link BarChartPreference#setBarViewInfos(BarViewInfo[])}
+ *
+ * <pre>
+ * BarViewInfo[] barViewsInfo = new BarViewInfo [] {
+ * new BarViewInfo(...),
+ * new BarViewInfo(...),
+ * new BarViewInfo(...),
+ * new BarViewInfo(...),
+ * };
+ *
+ * preference.setBarViewInfos(barViewsInfo);
* </pre>
*/
public class BarChartPreference extends Preference {
+ static final int MAXIMUM_BAR_VIEWS = 4;
private static final String TAG = "BarChartPreference";
- private static final int MAXIMUM_BAR_VIEWS = 4;
private static final int[] BAR_VIEWS = {
R.id.bar_view1,
R.id.bar_view2,
@@ -74,12 +87,7 @@
};
private int mMaxBarHeight;
- @StringRes
- private int mTitleId;
- @StringRes
- private int mDetailsId;
- private BarViewInfo[] mBarViewsInfo;
- private View.OnClickListener mDetailsOnClickListener;
+ private BarChartInfo mBarChartInfo;
public BarChartPreference(Context context) {
super(context);
@@ -103,40 +111,26 @@
}
/**
- * Set the text resource for bar chart title.
- */
- public void setBarChartTitle(@StringRes int resId) {
- mTitleId = resId;
- notifyChanged();
- }
-
- /**
- * Set the text resource for bar chart details.
- */
- public void setBarChartDetails(@StringRes int resId) {
- mDetailsId = resId;
- notifyChanged();
- }
-
- /**
- * Register a callback to be invoked when bar chart details view is clicked.
- */
- public void setBarChartDetailsClickListener(@Nullable View.OnClickListener clickListener) {
- mDetailsOnClickListener = clickListener;
- notifyChanged();
- }
-
- /**
- * Set all bar view information which you'd like to show in preference.
+ * According to the information in {@link BarChartInfo} to initialize bar chart.
*
- * @param barViewsInfo the barViewsInfo contain at least one {@link BarViewInfo}.
+ * @param barChartInfo The barChartInfo contains title, details, empty text, click listener
+ * attached on details view and four bar views.
*/
- public void setAllBarViewsInfo(@NonNull BarViewInfo[] barViewsInfo) {
- mBarViewsInfo = barViewsInfo;
- // Do a sort in descending order, the first element would have max {@link
- // BarViewInfo#mBarNumber}
- Arrays.sort(mBarViewsInfo);
- calculateAllBarViewHeights();
+ public void initializeBarChart(@NonNull BarChartInfo barChartInfo) {
+ mBarChartInfo = barChartInfo;
+ notifyChanged();
+ }
+
+ /**
+ * Sets all bar view information which you'd like to show in preference.
+ *
+ * @param barViewInfos the barViewInfos contain at least one {@link BarViewInfo}.
+ */
+ public void setBarViewInfos(@Nullable BarViewInfo[] barViewInfos) {
+ if (barViewInfos != null && barViewInfos.length > MAXIMUM_BAR_VIEWS) {
+ throw new IllegalStateException("We only support up to four bar views");
+ }
+ mBarChartInfo.setBarViewInfos(barViewInfos);
notifyChanged();
}
@@ -146,7 +140,17 @@
holder.setDividerAllowedAbove(true);
holder.setDividerAllowedBelow(true);
+ // We must show title of bar chart.
bindChartTitleView(holder);
+
+ final BarViewInfo[] barViewInfos = mBarChartInfo.getBarViewInfos();
+ // If there is no any bar view, we just show an empty text.
+ if (barViewInfos == null || barViewInfos.length == 0) {
+ setEmptyViewVisible(holder, true /* visible */);
+ return;
+ }
+ setEmptyViewVisible(holder, false /* visible */);
+
bindChartDetailsView(holder);
updateBarChart(holder);
}
@@ -160,43 +164,68 @@
private void bindChartTitleView(PreferenceViewHolder holder) {
final TextView titleView = (TextView) holder.findViewById(R.id.bar_chart_title);
- titleView.setText(mTitleId);
+ titleView.setText(mBarChartInfo.getTitle());
}
private void bindChartDetailsView(PreferenceViewHolder holder) {
final Button detailsView = (Button) holder.findViewById(R.id.bar_chart_details);
- if (mDetailsId == 0) {
+ final int details = mBarChartInfo.getDetails();
+ if (details == 0) {
detailsView.setVisibility(View.GONE);
} else {
detailsView.setVisibility(View.VISIBLE);
- detailsView.setText(mDetailsId);
- detailsView.setOnClickListener(mDetailsOnClickListener);
+ detailsView.setText(details);
+ detailsView.setOnClickListener(mBarChartInfo.getDetailsOnClickListener());
}
}
private void updateBarChart(PreferenceViewHolder holder) {
+ normalizeBarViewHeights();
+
+ final BarViewInfo[] barViewInfos = mBarChartInfo.getBarViewInfos();
+
for (int index = 0; index < MAXIMUM_BAR_VIEWS; index++) {
final BarView barView = (BarView) holder.findViewById(BAR_VIEWS[index]);
- // If there is no bar views data can be shown.
- if (mBarViewsInfo == null || index >= mBarViewsInfo.length) {
+ // If there is no bar view info can be shown.
+ if (barViewInfos == null || index >= barViewInfos.length) {
barView.setVisibility(View.GONE);
continue;
}
barView.setVisibility(View.VISIBLE);
- barView.updateView(mBarViewsInfo[index]);
+ barView.updateView(barViewInfos[index]);
}
}
- private void calculateAllBarViewHeights() {
+ private void normalizeBarViewHeights() {
+ final BarViewInfo[] barViewInfos = mBarChartInfo.getBarViewInfos();
+ // If there is no any bar view info, we don't need to calculate the height of all bar views.
+ if (barViewInfos == null || barViewInfos.length == 0) {
+ return;
+ }
+ // Do a sort in descending order, the first element would have max {@link
+ // BarViewInfo#mHeight}
+ Arrays.sort(barViewInfos);
// Since we sorted this array in advance, the first element must have the max {@link
// BarViewInfo#mHeight}.
- final int maxBarHeight = mBarViewsInfo[0].getHeight();
+ final int maxBarHeight = barViewInfos[0].getHeight();
// If the max number of bar view is zero, then we don't calculate the unit for bar height.
final int unit = maxBarHeight == 0 ? 0 : mMaxBarHeight / maxBarHeight;
- for (BarViewInfo barView : mBarViewsInfo) {
+ for (BarViewInfo barView : barViewInfos) {
barView.setNormalizedHeight(barView.getHeight() * unit);
}
}
+
+ private void setEmptyViewVisible(PreferenceViewHolder holder, boolean visible) {
+ final View barViewsContainer = holder.findViewById(R.id.bar_views_container);
+ final TextView emptyView = (TextView) holder.findViewById(R.id.empty_view);
+ final int emptyTextRes = mBarChartInfo.getEmptyText();
+
+ if (emptyTextRes != 0) {
+ emptyView.setText(emptyTextRes);
+ }
+ emptyView.setVisibility(visible ? View.VISIBLE : View.GONE);
+ barViewsContainer.setVisibility(visible ? View.GONE : View.VISIBLE);
+ }
}
diff --git a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml b/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
index cbebbb3..d6dc211 100644
--- a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
@@ -18,12 +18,11 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingMode="stack">
<item>
- <shape
- android:tint="?android:attr/colorForeground">
+ <shape>
<corners
android:radius="20dp"/>
<solid
- android:color="@android:color/transparent"/>
+ android:color="?android:attr/colorPrimary"/>
<stroke
android:color="#1f000000"
android:width="1dp"/>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 842779d..7dcc3ac 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1138,7 +1138,7 @@
<string name="notice_header" translatable="false"></string>
<!-- UI debug setting: opt in to use updated graphics driver? [CHAR LIMIT=100] -->
- <string name="updated_gfx_driver_dev_opt_in_app_summary">Opt in app to use updated graphcis driver in developement</string>
+ <string name="gup_dev_opt_in_app_summary">Opt in app to use Game Update Package in developement</string>
<!-- Name of the phone device [CHAR LIMIT=NONE] -->
<string name="media_transfer_phone_device_name">Phone speaker</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
index d737ced..4ac3ce4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
@@ -183,7 +183,7 @@
public final CharSequence contentDescription;
public final long accessFinishTime;
- private Access(String packageName, UserHandle userHandle, Drawable icon,
+ public Access(String packageName, UserHandle userHandle, Drawable icon,
CharSequence label, CharSequence contentDescription,
long accessFinishTime) {
this.packageName = packageName;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartInfoTest.java
new file mode 100644
index 0000000..29d57b7
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartInfoTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.view.View;
+
+import androidx.annotation.StringRes;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class BarChartInfoTest {
+
+ @Rule
+ public final ExpectedException thrown = ExpectedException.none();
+ @StringRes
+ private final int mTitle = 0x11111111;
+ @StringRes
+ private final int mDetails = 0x22222222;
+ @StringRes
+ private final int mEmptyText = 0x33333333;
+ private final View.OnClickListener mClickListener = v -> {
+ };
+
+ @Test
+ public void builder_shouldSetFieldsInTheInfo() {
+ final BarChartInfo barChartInfo = new BarChartInfo.Builder()
+ .setTitle(mTitle)
+ .setDetails(mDetails)
+ .setEmptyText(mEmptyText)
+ .setDetailsOnClickListener(mClickListener)
+ .build();
+ assertThat(barChartInfo.getTitle()).isEqualTo(mTitle);
+ assertThat(barChartInfo.getDetails()).isEqualTo(mDetails);
+ assertThat(barChartInfo.getEmptyText()).isEqualTo(mEmptyText);
+ assertThat(barChartInfo.getDetailsOnClickListener()).isEqualTo(mClickListener);
+ }
+
+ @Test
+ public void builder_noTitle_shouldThrowIllegalStateException() {
+ thrown.expect(IllegalStateException.class);
+
+ new BarChartInfo.Builder()
+ .setDetails(mDetails)
+ .setEmptyText(mEmptyText)
+ .setDetailsOnClickListener(mClickListener)
+ .build();
+ }
+
+ @Test
+ public void addBarViewInfo_oneBarViewInfo_shouldSetOneBarViewInfo() {
+ final BarViewInfo barViewInfo = new BarViewInfo(
+ null /* icon */,
+ 50,
+ mTitle);
+
+ final BarChartInfo mBarChartInfo = new BarChartInfo.Builder()
+ .setTitle(mTitle)
+ .setDetails(mDetails)
+ .setEmptyText(mEmptyText)
+ .setDetailsOnClickListener(mClickListener)
+ .addBarViewInfo(barViewInfo)
+ .build();
+
+ assertThat(mBarChartInfo.getBarViewInfos().length).isEqualTo(1);
+ assertThat(mBarChartInfo.getBarViewInfos()[0]).isEqualTo(barViewInfo);
+ }
+
+ @Test
+ public void addBarViewInfo_maxNumberOfInfoAllowed_shouldSetMaxBarViewInfos() {
+ final BarViewInfo barViewInfo = new BarViewInfo(
+ null /* icon */,
+ 50,
+ mTitle);
+ final BarChartInfo mBarChartInfo = new BarChartInfo.Builder()
+ .setTitle(mTitle)
+ .setDetails(mDetails)
+ .setEmptyText(mEmptyText)
+ .setDetailsOnClickListener(mClickListener)
+ .addBarViewInfo(barViewInfo)
+ .addBarViewInfo(barViewInfo)
+ .addBarViewInfo(barViewInfo)
+ .addBarViewInfo(barViewInfo)
+ .build();
+
+ assertThat(mBarChartInfo.getBarViewInfos().length).isEqualTo(4);
+ }
+
+ @Test
+ public void addBarViewInfo_moreInfosThanMaxAllowed_shouldThrowIllegalStateException() {
+ thrown.expect(IllegalStateException.class);
+
+ final BarViewInfo barViewInfo = new BarViewInfo(
+ null /* icon */,
+ 50,
+ mTitle);
+ new BarChartInfo.Builder()
+ .setTitle(mTitle)
+ .setDetails(mDetails)
+ .setEmptyText(mEmptyText)
+ .setDetailsOnClickListener(mClickListener)
+ .addBarViewInfo(barViewInfo)
+ .addBarViewInfo(barViewInfo)
+ .addBarViewInfo(barViewInfo)
+ .addBarViewInfo(barViewInfo)
+ .addBarViewInfo(barViewInfo)
+ .build();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
index 96e8995..cf6137d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
@@ -1,5 +1,5 @@
/*
- * 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.
@@ -26,7 +26,9 @@
import androidx.preference.PreferenceViewHolder;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@@ -34,6 +36,9 @@
@RunWith(RobolectricTestRunner.class)
public class BarChartPreferenceTest {
+ @Rule
+ public final ExpectedException thrown = ExpectedException.none();
+
private Context mContext;
private View mBarChartView;
private Drawable mIcon;
@@ -44,6 +49,7 @@
private TextView mDetailsView;
private PreferenceViewHolder mHolder;
private BarChartPreference mPreference;
+ private BarChartInfo mBarChartInfo;
@Before
public void setUp() {
@@ -51,21 +57,31 @@
mBarChartView = View.inflate(mContext, R.layout.settings_bar_chart, null /* parent */);
mHolder = PreferenceViewHolder.createInstanceForTests(mBarChartView);
mPreference = new BarChartPreference(mContext, null /* attrs */);
- mPreference.setBarChartTitle(R.string.debug_app);
mIcon = mContext.getDrawable(R.drawable.ic_menu);
- mBarView1 = (BarView) mBarChartView.findViewById(R.id.bar_view1);
- mBarView2 = (BarView) mBarChartView.findViewById(R.id.bar_view2);
- mBarView3 = (BarView) mBarChartView.findViewById(R.id.bar_view3);
- mBarView4 = (BarView) mBarChartView.findViewById(R.id.bar_view4);
- mDetailsView = (TextView) mBarChartView.findViewById(R.id.bar_chart_details);
+ mBarView1 = mBarChartView.findViewById(R.id.bar_view1);
+ mBarView2 = mBarChartView.findViewById(R.id.bar_view2);
+ mBarView3 = mBarChartView.findViewById(R.id.bar_view3);
+ mBarView4 = mBarChartView.findViewById(R.id.bar_view4);
+ mDetailsView = mBarChartView.findViewById(R.id.bar_chart_details);
+
+ mBarChartInfo = new BarChartInfo.Builder()
+ .setTitle(R.string.debug_app)
+ .setDetails(R.string.debug_app)
+ .setEmptyText(R.string.debug_app)
+ .setDetailsOnClickListener(v -> {
+ })
+ .build();
}
@Test
- public void setBarChartTitleRes_setTitleRes_showInBarChartTitle() {
- final TextView titleView = (TextView) mBarChartView.findViewById(R.id.bar_chart_title);
+ public void initializeBarChart_titleSet_shouldSetTitleInChartView() {
+ final TextView titleView = mBarChartView.findViewById(R.id.bar_chart_title);
+ final BarChartInfo barChartInfo = new BarChartInfo.Builder()
+ .setTitle(R.string.debug_app)
+ .build();
- mPreference.setBarChartTitle(R.string.debug_app);
+ mPreference.initializeBarChart(barChartInfo);
mPreference.onBindViewHolder(mHolder);
assertThat(titleView.getVisibility()).isEqualTo(View.VISIBLE);
@@ -73,21 +89,33 @@
}
@Test
- public void onBindViewHolder_notSetDetailsRes_barChartDetailsViewIsGone() {
- // We don't call BarChartPreference#setBarChartDetails
+ public void initializeBarChart_noBarViewSet_shouldShowTitleAndEmptyView() {
+ final BarChartInfo barChartInfo = new BarChartInfo.Builder()
+ .setTitle(R.string.debug_app)
+ .setEmptyText(R.string.debug_app)
+ .build();
+
+ mPreference.initializeBarChart(barChartInfo);
+ // We don't add any bar view yet.
mPreference.onBindViewHolder(mHolder);
- assertThat(mDetailsView.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mBarChartView.findViewById(R.id.bar_chart_title).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mBarChartView.findViewById(R.id.empty_view).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mBarChartView.findViewById(R.id.bar_views_container).getVisibility())
+ .isEqualTo(View.GONE);
}
@Test
- public void onBindViewHolder_notSetDetailsRes_barChartDetailsViewIsGoneThenReappears() {
- // We don't call BarChartPreference#setBarChartDetails yet.
- mPreference.onBindViewHolder(mHolder);
+ public void initializeBarChart_detailsSet_shouldShowBarChartDetailsView() {
+ final BarChartInfo barChartInfo = new BarChartInfo.Builder()
+ .setTitle(R.string.debug_app)
+ .setDetails(R.string.debug_app)
+ .addBarViewInfo(new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app))
+ .build();
- assertThat(mDetailsView.getVisibility()).isEqualTo(View.GONE);
-
- mPreference.setBarChartDetails(R.string.debug_app);
+ mPreference.initializeBarChart(barChartInfo);
mPreference.onBindViewHolder(mHolder);
assertThat(mDetailsView.getVisibility()).isEqualTo(View.VISIBLE);
@@ -95,19 +123,30 @@
}
@Test
- public void setBarChartDetailsRes_setDetailsRes_showInBarChartDetails() {
- mPreference.setBarChartDetails(R.string.debug_app);
+ public void initializeBarChart_detailsNotSet_shouldHideBarChartDetailsView() {
+ // We don't call BarChartInfo.Builder#setDetails yet.
+ final BarChartInfo barChartInfo = new BarChartInfo.Builder()
+ .setTitle(R.string.debug_app)
+ .addBarViewInfo(new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app))
+ .build();
+
+ mPreference.initializeBarChart(barChartInfo);
mPreference.onBindViewHolder(mHolder);
- assertThat(mDetailsView.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mDetailsView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
+ assertThat(mDetailsView.getVisibility()).isEqualTo(View.GONE);
}
@Test
- public void setBarChartDetailsClickListener_setClickListener_detailsViewAttachClickListener() {
- mPreference.setBarChartDetails(R.string.debug_app);
- mPreference.setBarChartDetailsClickListener(v -> {
- });
+ public void initializeBarChart_clickListenerSet_shouldSetClickListenerOnDetailsView() {
+ final BarChartInfo barChartInfo = new BarChartInfo.Builder()
+ .setTitle(R.string.debug_app)
+ .setDetails(R.string.debug_app)
+ .setDetailsOnClickListener(v -> {
+ })
+ .addBarViewInfo(new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app))
+ .build();
+
+ mPreference.initializeBarChart(barChartInfo);
mPreference.onBindViewHolder(mHolder);
assertThat(mDetailsView.getVisibility()).isEqualTo(View.VISIBLE);
@@ -115,12 +154,13 @@
}
@Test
- public void setAllBarViewsInfo_setOneBarViewInfo_showOneBarView() {
+ public void setBarViewInfos_oneBarViewInfoSet_shouldShowOneBarView() {
final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app)
};
- mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.initializeBarChart(mBarChartInfo);
+ mPreference.setBarViewInfos(barViewsInfo);
mPreference.onBindViewHolder(mHolder);
assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
@@ -132,13 +172,14 @@
}
@Test
- public void setAllBarViewsInfo_setTwoBarViewsInfo_showTwoBarViews() {
+ public void setBarViewInfos_twoBarViewInfosSet_shouldShowTwoBarViews() {
final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app),
new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app)
};
- mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.initializeBarChart(mBarChartInfo);
+ mPreference.setBarViewInfos(barViewsInfo);
mPreference.onBindViewHolder(mHolder);
assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
@@ -151,14 +192,15 @@
}
@Test
- public void setAllBarViewsInfo_setThreeBarViewsInfo_showThreeBarViews() {
+ public void setBarViewInfos_threeBarViewInfosSet_shouldShowThreeBarViews() {
final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app),
new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app)
};
- mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.initializeBarChart(mBarChartInfo);
+ mPreference.setBarViewInfos(barViewsInfo);
mPreference.onBindViewHolder(mHolder);
assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
@@ -172,7 +214,7 @@
}
@Test
- public void setAllBarViewsInfo_setFourBarViewsInfo_showFourBarViews() {
+ public void setBarViewInfos_fourBarViewInfosSet_shouldShowFourBarViews() {
final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app),
new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
@@ -180,7 +222,8 @@
new BarViewInfo(mIcon, 2 /* barNumber */, R.string.debug_app),
};
- mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.initializeBarChart(mBarChartInfo);
+ mPreference.setBarViewInfos(barViewsInfo);
mPreference.onBindViewHolder(mHolder);
assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
@@ -194,7 +237,22 @@
}
@Test
- public void setAllBarViewsInfo_setFourBarViewsInfo_barViewWasSortedInDescending() {
+ public void setBarViewInfos_moreInfosThanMaxAllowed_shouldThrowIllegalStateException() {
+ thrown.expect(IllegalStateException.class);
+
+ final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+ new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 50 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 70 /* barNumber */, R.string.debug_app),
+ };
+
+ mPreference.setBarViewInfos(barViewsInfo);
+ }
+
+ @Test
+ public void setBarViewInfos_barViewInfosSet_shouldBeSortedInDescending() {
final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app),
new BarViewInfo(mIcon, 50 /* barNumber */, R.string.debug_app),
@@ -202,7 +260,8 @@
new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
};
- mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.initializeBarChart(mBarChartInfo);
+ mPreference.setBarViewInfos(barViewsInfo);
mPreference.onBindViewHolder(mHolder);
assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
@@ -216,12 +275,13 @@
}
@Test
- public void setAllBarViewsInfo_setValidSummaryRes_barViewShouldShowSummary() {
+ public void setBarViewInfos_validBarViewSummarySet_barViewShouldShowSummary() {
final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
};
- mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.initializeBarChart(mBarChartInfo);
+ mPreference.setBarViewInfos(barViewsInfo);
mPreference.onBindViewHolder(mHolder);
assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
@@ -229,13 +289,14 @@
}
@Test
- public void setAllBarViewsInfo_setClickListenerForBarView_barViewAttachClickListener() {
+ public void setBarViewInfos_clickListenerForBarViewSet_barViewShouldHaveClickListener() {
final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app);
viewInfo.setClickListener(v -> {
});
final BarViewInfo[] barViewsInfo = new BarViewInfo[]{viewInfo};
- mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.initializeBarChart(mBarChartInfo);
+ mPreference.setBarViewInfos(barViewsInfo);
mPreference.onBindViewHolder(mHolder);
assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index e3d3d81..526efcb 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -705,8 +705,11 @@
Settings.Global.GPU_DEBUG_LAYERS_GLES,
GlobalSettingsProto.Gpu.DEBUG_LAYERS_GLES);
dumpSetting(s, p,
- Settings.Global.UPDATED_GFX_DRIVER_DEV_OPT_IN_APP,
- GlobalSettingsProto.Gpu.UPDATED_GFX_DRIVER_DEV_OPT_IN_APP);
+ Settings.Global.GUP_DEV_OPT_IN_APPS,
+ GlobalSettingsProto.Gpu.GUP_DEV_OPT_IN_APPS);
+ dumpSetting(s, p,
+ Settings.Global.GUP_BLACK_LIST,
+ GlobalSettingsProto.Gpu.GUP_BLACK_LIST);
p.end(gpuToken);
final long hdmiToken = p.start(GlobalSettingsProto.HDMI);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index ce529a0..1727e75 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -4125,10 +4125,12 @@
Secure.CHARGING_SOUNDS_ENABLED);
if (!globalChargingSoundEnabled.isNull()) {
- secureSettings.insertSettingLocked(
- Secure.CHARGING_SOUNDS_ENABLED,
- globalChargingSoundEnabled.getValue(), null, false,
- SettingsState.SYSTEM_PACKAGE_NAME);
+ if (secureChargingSoundsEnabled.isNull()) {
+ secureSettings.insertSettingLocked(
+ Secure.CHARGING_SOUNDS_ENABLED,
+ globalChargingSoundEnabled.getValue(), null, false,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
// set global charging_sounds_enabled setting to null since it's deprecated
globalSettings.insertSettingLocked(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 8cfc2a6..bff2c84 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -82,6 +82,7 @@
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
<uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
+ <uses-permission android:name="android.permission.MANAGE_ROLLBACKS" />
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java
new file mode 100644
index 0000000..c7bc858
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.plugins;
+
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.plugins.annotations.DependsOn;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Dispatches events to {@link DarkReceiver}s about changes in darkness, tint area and dark
+ * intensity. Accessible through {@link PluginDependency}
+ */
+@ProvidesInterface(version = DarkIconDispatcher.VERSION)
+@DependsOn(target = DarkReceiver.class)
+public interface DarkIconDispatcher {
+ int VERSION = 1;
+
+ /**
+ * Sets the dark area so {@link #applyDark} only affects the icons in the specified area.
+ *
+ * @param r the area in which icons should change its tint, in logical screen
+ * coordinates
+ */
+ void setIconsDarkArea(Rect r);
+
+ /**
+ * Adds a receiver to receive callbacks onDarkChanged
+ */
+ void addDarkReceiver(DarkReceiver receiver);
+
+ /**
+ * Adds a receiver to receive callbacks onDarkChanged
+ */
+ void addDarkReceiver(ImageView imageView);
+
+ /**
+ * Must have been previously been added through one of the addDarkReceive methods above.
+ */
+ void removeDarkReceiver(DarkReceiver object);
+
+ /**
+ * Must have been previously been added through one of the addDarkReceive methods above.
+ */
+ void removeDarkReceiver(ImageView object);
+
+ /**
+ * Used to reapply darkness on an object, must have previously been added through
+ * addDarkReceiver.
+ */
+ void applyDark(DarkReceiver object);
+
+ int DEFAULT_ICON_TINT = Color.WHITE;
+ Rect sTmpRect = new Rect();
+ int[] sTmpInt2 = new int[2];
+
+ /**
+ * @return the tint to apply to view depending on the desired tint color and
+ * the screen tintArea in which to apply that tint
+ */
+ static int getTint(Rect tintArea, View view, int color) {
+ if (isInArea(tintArea, view)) {
+ return color;
+ } else {
+ return DEFAULT_ICON_TINT;
+ }
+ }
+
+ /**
+ * @return the dark intensity to apply to view depending on the desired dark
+ * intensity and the screen tintArea in which to apply that intensity
+ */
+ static float getDarkIntensity(Rect tintArea, View view, float intensity) {
+ if (isInArea(tintArea, view)) {
+ return intensity;
+ } else {
+ return 0f;
+ }
+ }
+
+ /**
+ * @return true if more than half of the view area are in area, false
+ * otherwise
+ */
+ static boolean isInArea(Rect area, View view) {
+ if (area.isEmpty()) {
+ return true;
+ }
+ sTmpRect.set(area);
+ view.getLocationOnScreen(sTmpInt2);
+ int left = sTmpInt2[0];
+
+ int intersectStart = Math.max(left, area.left);
+ int intersectEnd = Math.min(left + view.getWidth(), area.right);
+ int intersectAmount = Math.max(0, intersectEnd - intersectStart);
+
+ boolean coversFullStatusBar = area.top <= 0;
+ boolean majorityOfWidth = 2 * intersectAmount > view.getWidth();
+ return majorityOfWidth && coversFullStatusBar;
+ }
+
+ /**
+ * Receives a callback on darkness changes
+ */
+ @ProvidesInterface(version = DarkReceiver.VERSION)
+ interface DarkReceiver {
+ int VERSION = 1;
+ void onDarkChanged(Rect area, float darkIntensity, int tint);
+ }
+}
diff --git a/packages/SystemUI/res/drawable/face_dialog_error_to_face.xml b/packages/SystemUI/res/drawable/face_dialog_error_to_face.xml
new file mode 100644
index 0000000..75311f4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/face_dialog_error_to_face.xml
@@ -0,0 +1,517 @@
+<?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
+ -->
+
+<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:width="60dp"
+ android:height="60dp"
+ android:viewportWidth="60"
+ android:viewportHeight="60">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G_N_1_T_0"
+ android:translateX="30"
+ android:translateY="30">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="-30"
+ android:translateY="-30">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
+ android:strokeWidth="2.5"
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorError"
+ android:trimPathStart="0"
+ android:trimPathEnd="1"
+ android:trimPathOffset="0" />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:pathData=" M34.78 38.76 C33.83,38.75 31.54,38.75 30.01,38.75 C26.97,38.75 26.14,38.75 24.3,38.76 "
+ android:strokeWidth="2.5"
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorError"
+ android:trimPathStart="0.34"
+ android:trimPathEnd="0.5700000000000001"
+ android:trimPathOffset="0" />
+ <group
+ android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0"
+ android:scaleX="0.3"
+ android:scaleY="0.3"
+ android:translateX="37.788"
+ android:translateY="19.53">
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="0"
+ android:fillColor="@color/biometric_face_icon_gray"
+ android:fillType="nonZero"
+ android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.1,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0"
+ android:scaleX="0.3"
+ android:scaleY="0.3"
+ android:translateX="22.005"
+ android:translateY="19.51">
+ <path
+ android:name="_R_G_L_0_G_D_3_P_0"
+ android:fillAlpha="0"
+ android:fillColor="@color/biometric_face_icon_gray"
+ android:fillType="nonZero"
+ android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.2,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0"
+ android:translateX="30.3"
+ android:translateY="29.215">
+ <path
+ android:name="_R_G_L_0_G_D_4_P_0"
+ android:fillAlpha="1"
+ android:fillColor="?android:attr/colorError"
+ android:fillType="nonZero"
+ android:pathData=" M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-11.71 -1.5,-11.71 C-1.5,-11.71 0.9,-11.71 0.9,-11.71 C0.9,-11.71 0.9,3.25 0.9,3.25c " />
+ </group>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="strokeColor"
+ android:startOffset="0"
+ android:valueFrom="?android:attr/colorError"
+ android:valueTo="?android:attr/colorError"
+ android:valueType="colorType">
+ <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:duration="17"
+ android:propertyName="strokeColor"
+ android:startOffset="83"
+ android:valueFrom="?android:attr/colorError"
+ android:valueTo="@color/biometric_face_icon_gray"
+ android:valueType="colorType">
+ <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_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="strokeColor"
+ android:startOffset="0"
+ android:valueFrom="?android:attr/colorError"
+ android:valueTo="?android:attr/colorError"
+ android:valueType="colorType">
+ <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:duration="17"
+ android:propertyName="strokeColor"
+ android:startOffset="83"
+ android:valueFrom="?android:attr/colorError"
+ android:valueTo="@color/biometric_face_icon_gray"
+ android:valueType="colorType">
+ <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_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="217"
+ android:propertyName="strokeWidth"
+ android:startOffset="0"
+ android:valueFrom="2.5"
+ android:valueTo="2"
+ 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_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="217"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M34.78 38.76 C33.83,38.75 31.54,38.75 30.01,38.75 C26.97,38.75 26.14,38.75 24.3,38.76 "
+ android:valueTo="M33.75 42.75 C32.75,43.76 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
+ android:valueType="pathType">
+ <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_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="217"
+ android:propertyName="trimPathStart"
+ android:startOffset="0"
+ android:valueFrom="0.34"
+ android:valueTo="0"
+ 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_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="217"
+ android:propertyName="trimPathEnd"
+ android:startOffset="0"
+ android:valueFrom="0.5700000000000001"
+ android:valueTo="1"
+ 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_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <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:duration="17"
+ android:propertyName="fillAlpha"
+ android:startOffset="83"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <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_D_2_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:pathData="M 37.788,19.53C 38.3400184636116,20.241653709411622 37.235981536388394,18.81834629058838 37.788,19.53"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="0">
+ <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>
+ <objectAnimator
+ android:duration="167"
+ android:pathData="M 37.788,19.53C 38.3400184636116,20.241653709411622 40.5479815363884,23.08834629058838 41.1,23.8"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="50">
+ <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_2_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="0.3"
+ android:valueTo="0.3"
+ 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>
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="0.3"
+ android:valueTo="0.3"
+ 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>
+ <objectAnimator
+ android:duration="117"
+ android:propertyName="scaleX"
+ android:startOffset="50"
+ android:valueFrom="0.3"
+ android:valueTo="1"
+ 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>
+ <objectAnimator
+ android:duration="117"
+ android:propertyName="scaleY"
+ android:startOffset="50"
+ android:valueFrom="0.3"
+ android:valueTo="1"
+ 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_3_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <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:duration="17"
+ android:propertyName="fillAlpha"
+ android:startOffset="83"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <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_D_3_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:pathData="M 22.005,19.51C 21.43742198228836,20.224974105358122 22.57257801771164,18.79502589464188 22.005,19.51"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="0">
+ <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>
+ <objectAnimator
+ android:duration="167"
+ android:pathData="M 22.005,19.51C 21.43742198228836,20.224974105358122 19.16757801771164,23.08502589464188 18.6,23.8"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="50">
+ <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_3_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="0.3"
+ android:valueTo="0.3"
+ 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>
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="0.3"
+ android:valueTo="0.3"
+ 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>
+ <objectAnimator
+ android:duration="117"
+ android:propertyName="scaleX"
+ android:startOffset="50"
+ android:valueFrom="0.3"
+ android:valueTo="1"
+ 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>
+ <objectAnimator
+ android:duration="117"
+ android:propertyName="scaleY"
+ android:startOffset="50"
+ android:valueFrom="0.3"
+ android:valueTo="1"
+ 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_4_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="?android:attr/colorError"
+ android:valueTo="?android:attr/colorError"
+ android:valueType="colorType">
+ <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:duration="17"
+ android:propertyName="fillColor"
+ android:startOffset="83"
+ android:valueFrom="?android:attr/colorError"
+ android:valueTo="@color/biometric_face_icon_gray"
+ android:valueType="colorType">
+ <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_D_4_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="150"
+ android:pathData="M 30.3,29.215C 30.3,29.58759101867676 30.3,31.077408981323238 30.3,31.45"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="0">
+ <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_4_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-11.71 -1.5,-11.71 C-1.5,-11.71 0.9,-11.71 0.9,-11.71 C0.9,-11.71 0.9,3.25 0.9,3.25c "
+ android:valueTo="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-3.25 -1.5,-3.25 C-1.5,-3.25 0.9,-3.25 0.9,-3.25 C0.9,-3.25 0.9,3.25 0.9,3.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.321,0 0.67,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="133"
+ android:propertyName="pathData"
+ android:startOffset="83"
+ android:valueFrom="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-3.25 -1.5,-3.25 C-1.5,-3.25 0.9,-3.25 0.9,-3.25 C0.9,-3.25 0.9,3.25 0.9,3.25c "
+ android:valueTo="M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.568,0 0.456,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:duration="233"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_face_blue_to_checkmark.xml b/packages/SystemUI/res/drawable/face_dialog_face_blue_to_checkmark.xml
new file mode 100644
index 0000000..e4ace67
--- /dev/null
+++ b/packages/SystemUI/res/drawable/face_dialog_face_blue_to_checkmark.xml
@@ -0,0 +1,637 @@
+<?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
+ -->
+
+<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:width="60dp"
+ android:height="60dp"
+ android:viewportWidth="60"
+ android:viewportHeight="60">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G_N_2_T_0"
+ android:translateX="30"
+ android:translateY="30">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="-30"
+ android:translateY="-30">
+ <group
+ android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+ android:scaleX="0.08"
+ android:scaleY="0.08"
+ android:translateX="30.1"
+ android:translateY="30.083">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="0"
+ android:fillColor="?android:attr/colorAccent"
+ android:fillType="nonZero"
+ android:pathData=" M-116 -16.5 C-116,-16.5 -31.25,68.5 -31.25,68.5 C-31.25,68.5 108.75,-71.5 108.75,-71.5 "
+ android:trimPathStart="0"
+ android:trimPathEnd="0"
+ android:trimPathOffset="0" />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0"
+ android:scaleX="0.08"
+ android:scaleY="0.08"
+ android:translateX="30.1"
+ android:translateY="30.083">
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:pathData=" M-116 -16.5 C-116,-16.5 -31.25,68.5 -31.25,68.5 C-31.25,68.5 108.75,-71.5 108.75,-71.5 "
+ android:strokeWidth="20"
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorAccent"
+ android:trimPathStart="0"
+ android:trimPathEnd="0"
+ android:trimPathOffset="0" />
+ </group>
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
+ android:strokeWidth="2.5"
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorAccent"
+ android:trimPathStart="0"
+ android:trimPathEnd="1"
+ android:trimPathOffset="0" />
+ <group
+ android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0"
+ android:pivotX="1.05"
+ android:pivotY="-9.891"
+ android:scaleX="1"
+ android:scaleY="1"
+ android:translateX="29.044"
+ android:translateY="41.647">
+ <path
+ android:name="_R_G_L_0_G_D_3_P_0"
+ android:pathData=" M4.71 1.1 C3.71,2.12 2.32,2.75 0.79,2.75 C-2.25,2.75 -4.7,0.29 -4.7,-2.75 "
+ android:strokeWidth="2"
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorAccent"
+ android:trimPathStart="0"
+ android:trimPathEnd="1"
+ android:trimPathOffset="0" />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0"
+ android:scaleX="1"
+ android:scaleY="1"
+ android:translateX="41.1"
+ android:translateY="23.8">
+ <path
+ android:name="_R_G_L_0_G_D_4_P_0"
+ android:fillAlpha="1"
+ android:fillColor="?android:attr/colorAccent"
+ android:fillType="nonZero"
+ android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.1,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_5_P_0_G_0_T_0"
+ android:scaleX="1"
+ android:scaleY="1"
+ android:translateX="18.6"
+ android:translateY="23.8">
+ <path
+ android:name="_R_G_L_0_G_D_5_P_0"
+ android:fillAlpha="1"
+ android:fillColor="?android:attr/colorAccent"
+ android:fillType="nonZero"
+ android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.2,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_6_P_0_G_0_T_0"
+ android:scaleX="1"
+ android:scaleY="1"
+ android:translateX="30.727"
+ android:translateY="31.703">
+ <path
+ android:name="_R_G_L_0_G_D_6_P_0"
+ android:fillAlpha="1"
+ android:fillColor="?android:attr/colorAccent"
+ android:fillType="nonZero"
+ android:pathData=" M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c " />
+ </group>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="0.08"
+ android:valueTo="0.08"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="0.08"
+ android:valueTo="0.08"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="scaleX"
+ android:startOffset="33"
+ android:valueFrom="0.08"
+ android:valueTo="0.12789"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="scaleY"
+ android:startOffset="33"
+ android:valueFrom="0.08"
+ android:valueTo="0.12789"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleX"
+ android:startOffset="200"
+ android:valueFrom="0.12789"
+ android:valueTo="0.12241"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleY"
+ android:startOffset="200"
+ android:valueFrom="0.12789"
+ android:valueTo="0.12241"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleX"
+ android:startOffset="300"
+ android:valueFrom="0.12241"
+ android:valueTo="0.125"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleY"
+ android:startOffset="300"
+ android:valueFrom="0.12241"
+ android:valueTo="0.125"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,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:duration="33"
+ android:propertyName="trimPathEnd"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="233"
+ android:propertyName="trimPathEnd"
+ android:startOffset="33"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="0.08"
+ android:valueTo="0.08"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="0.08"
+ android:valueTo="0.08"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="scaleX"
+ android:startOffset="33"
+ android:valueFrom="0.08"
+ android:valueTo="0.12789"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="scaleY"
+ android:startOffset="33"
+ android:valueFrom="0.08"
+ android:valueTo="0.12789"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleX"
+ android:startOffset="200"
+ android:valueFrom="0.12789"
+ android:valueTo="0.12241"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleY"
+ android:startOffset="200"
+ android:valueFrom="0.12789"
+ android:valueTo="0.12241"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleX"
+ android:startOffset="300"
+ android:valueFrom="0.12241"
+ android:valueTo="0.125"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleY"
+ android:startOffset="300"
+ android:valueFrom="0.12241"
+ android:valueTo="0.125"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="trimPathEnd"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="233"
+ android:propertyName="trimPathEnd"
+ android:startOffset="33"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="strokeColor"
+ android:startOffset="0"
+ android:valueFrom="?android:attr/colorAccent"
+ android:valueTo="?android:attr/colorAccent"
+ android:valueType="colorType">
+ <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:duration="17"
+ android:propertyName="strokeColor"
+ android:startOffset="67"
+ android:valueFrom="?android:attr/colorAccent"
+ android:valueTo="?android:attr/colorAccent"
+ android:valueType="colorType">
+ <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_D_3_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="strokeAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <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_D_3_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0.65"
+ 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>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0.65"
+ 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_3_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="trimPathStart"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0.5"
+ 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_3_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="trimPathEnd"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0.5"
+ 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_4_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <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_D_4_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ 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>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ 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_5_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <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_D_5_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ 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>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ 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_6_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <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_D_6_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.287,0.12 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.287,0.12 0.667,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:duration="383"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_face_gray_to_checkmark.xml b/packages/SystemUI/res/drawable/face_dialog_face_gray_to_checkmark.xml
new file mode 100644
index 0000000..b09f69b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/face_dialog_face_gray_to_checkmark.xml
@@ -0,0 +1,637 @@
+<?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
+ -->
+
+<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:width="60dp"
+ android:height="60dp"
+ android:viewportWidth="60"
+ android:viewportHeight="60">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G_N_2_T_0"
+ android:translateX="30"
+ android:translateY="30">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="-30"
+ android:translateY="-30">
+ <group
+ android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+ android:scaleX="0.08"
+ android:scaleY="0.08"
+ android:translateX="30.1"
+ android:translateY="30.083">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="0"
+ android:fillColor="@color/biometric_face_icon_gray"
+ android:fillType="nonZero"
+ android:pathData=" M-116 -16.5 C-116,-16.5 -31.25,68.5 -31.25,68.5 C-31.25,68.5 108.75,-71.5 108.75,-71.5 "
+ android:trimPathStart="0"
+ android:trimPathEnd="0"
+ android:trimPathOffset="0" />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0"
+ android:scaleX="0.08"
+ android:scaleY="0.08"
+ android:translateX="30.1"
+ android:translateY="30.083">
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:pathData=" M-116 -16.5 C-116,-16.5 -31.25,68.5 -31.25,68.5 C-31.25,68.5 108.75,-71.5 108.75,-71.5 "
+ android:strokeWidth="20"
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorAccent"
+ android:trimPathStart="0"
+ android:trimPathEnd="0"
+ android:trimPathOffset="0" />
+ </group>
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
+ android:strokeWidth="2.5"
+ android:strokeAlpha="1"
+ android:strokeColor="@color/biometric_face_icon_gray"
+ android:trimPathStart="0"
+ android:trimPathEnd="1"
+ android:trimPathOffset="0" />
+ <group
+ android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0"
+ android:pivotX="1.05"
+ android:pivotY="-9.891"
+ android:scaleX="1"
+ android:scaleY="1"
+ android:translateX="29.044"
+ android:translateY="41.647">
+ <path
+ android:name="_R_G_L_0_G_D_3_P_0"
+ android:pathData=" M4.71 1.1 C3.71,2.12 2.32,2.75 0.79,2.75 C-2.25,2.75 -4.7,0.29 -4.7,-2.75 "
+ android:strokeWidth="2"
+ android:strokeAlpha="1"
+ android:strokeColor="@color/biometric_face_icon_gray"
+ android:trimPathStart="0"
+ android:trimPathEnd="1"
+ android:trimPathOffset="0" />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0"
+ android:scaleX="1"
+ android:scaleY="1"
+ android:translateX="41.1"
+ android:translateY="23.8">
+ <path
+ android:name="_R_G_L_0_G_D_4_P_0"
+ android:fillAlpha="1"
+ android:fillColor="@color/biometric_face_icon_gray"
+ android:fillType="nonZero"
+ android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.1,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_5_P_0_G_0_T_0"
+ android:scaleX="1"
+ android:scaleY="1"
+ android:translateX="18.6"
+ android:translateY="23.8">
+ <path
+ android:name="_R_G_L_0_G_D_5_P_0"
+ android:fillAlpha="1"
+ android:fillColor="@color/biometric_face_icon_gray"
+ android:fillType="nonZero"
+ android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.2,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_6_P_0_G_0_T_0"
+ android:scaleX="1"
+ android:scaleY="1"
+ android:translateX="30.727"
+ android:translateY="31.703">
+ <path
+ android:name="_R_G_L_0_G_D_6_P_0"
+ android:fillAlpha="1"
+ android:fillColor="@color/biometric_face_icon_gray"
+ android:fillType="nonZero"
+ android:pathData=" M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c " />
+ </group>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="0.08"
+ android:valueTo="0.08"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="0.08"
+ android:valueTo="0.08"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="scaleX"
+ android:startOffset="33"
+ android:valueFrom="0.08"
+ android:valueTo="0.12789"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="scaleY"
+ android:startOffset="33"
+ android:valueFrom="0.08"
+ android:valueTo="0.12789"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleX"
+ android:startOffset="200"
+ android:valueFrom="0.12789"
+ android:valueTo="0.12241"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleY"
+ android:startOffset="200"
+ android:valueFrom="0.12789"
+ android:valueTo="0.12241"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleX"
+ android:startOffset="300"
+ android:valueFrom="0.12241"
+ android:valueTo="0.125"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleY"
+ android:startOffset="300"
+ android:valueFrom="0.12241"
+ android:valueTo="0.125"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,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:duration="33"
+ android:propertyName="trimPathEnd"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="233"
+ android:propertyName="trimPathEnd"
+ android:startOffset="33"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="0.08"
+ android:valueTo="0.08"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="0.08"
+ android:valueTo="0.08"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="scaleX"
+ android:startOffset="33"
+ android:valueFrom="0.08"
+ android:valueTo="0.12789"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="scaleY"
+ android:startOffset="33"
+ android:valueFrom="0.08"
+ android:valueTo="0.12789"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleX"
+ android:startOffset="200"
+ android:valueFrom="0.12789"
+ android:valueTo="0.12241"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleY"
+ android:startOffset="200"
+ android:valueFrom="0.12789"
+ android:valueTo="0.12241"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleX"
+ android:startOffset="300"
+ android:valueFrom="0.12241"
+ android:valueTo="0.125"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleY"
+ android:startOffset="300"
+ android:valueFrom="0.12241"
+ android:valueTo="0.125"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="trimPathEnd"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="233"
+ android:propertyName="trimPathEnd"
+ android:startOffset="33"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="strokeColor"
+ android:startOffset="0"
+ android:valueFrom="@color/biometric_face_icon_gray"
+ android:valueTo="@color/biometric_face_icon_gray"
+ android:valueType="colorType">
+ <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:duration="17"
+ android:propertyName="strokeColor"
+ android:startOffset="67"
+ android:valueFrom="@color/biometric_face_icon_gray"
+ android:valueTo="?android:attr/colorAccent"
+ android:valueType="colorType">
+ <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_D_3_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="strokeAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <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_D_3_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0.65"
+ 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>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0.65"
+ 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_3_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="trimPathStart"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0.5"
+ 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_3_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="trimPathEnd"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0.5"
+ 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_4_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <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_D_4_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ 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>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ 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_5_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <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_D_5_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ 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>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ 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_6_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <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_D_6_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.287,0.12 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.287,0.12 0.667,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:duration="383"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_face_gray_to_face_blue.xml b/packages/SystemUI/res/drawable/face_dialog_face_gray_to_face_blue.xml
new file mode 100644
index 0000000..9259dc7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/face_dialog_face_gray_to_face_blue.xml
@@ -0,0 +1,178 @@
+<?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"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="60dp"
+ android:height="60dp"
+ android:viewportHeight="60"
+ android:viewportWidth="60">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G_N_1_T_0"
+ android:translateX="30"
+ android:translateY="30">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="-30"
+ android:translateY="-30">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
+ android:strokeAlpha="1"
+ android:strokeColor="@color/biometric_face_icon_gray"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2.5"
+ android:trimPathEnd="1"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:pathData=" M33.75 42.75 C32.75,43.76 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
+ android:strokeAlpha="1"
+ android:strokeColor="@color/biometric_face_icon_gray"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2"
+ android:trimPathEnd="1"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="1"
+ android:fillColor="@color/biometric_face_icon_gray"
+ android:fillType="nonZero"
+ android:pathData=" M39 23.8 C39,25 39.9,25.9 41.1,25.9 C42.2,25.9 43.2,25 43.2,23.8 C43.2,22.6 42.3,21.7 41.1,21.7 C39.9,21.7 39,22.6 39,23.8c " />
+ <path
+ android:name="_R_G_L_0_G_D_3_P_0"
+ android:fillAlpha="1"
+ android:fillColor="@color/biometric_face_icon_gray"
+ android:fillType="nonZero"
+ android:pathData=" M16.5 23.8 C16.5,25 17.4,25.9 18.6,25.9 C19.8,25.9 20.7,25 20.7,23.8 C20.7,22.6 19.8,21.7 18.6,21.7 C17.4,21.7 16.5,22.6 16.5,23.8c " />
+ <path
+ android:name="_R_G_L_0_G_D_4_P_0"
+ android:fillAlpha="1"
+ android:fillColor="@color/biometric_face_icon_gray"
+ android:fillType="nonZero"
+ android:pathData=" M32.9 34.7 C32.9,34.7 27.7,34.7 27.7,34.7 C27.7,34.7 27.7,32.7 27.7,32.7 C27.7,32.7 30.9,32.7 30.9,32.7 C30.9,32.7 30.9,28.2 30.9,28.2 C30.9,28.2 32.9,28.2 32.9,28.2 C32.9,28.2 32.9,34.7 32.9,34.7c " />
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="strokeColor"
+ android:startOffset="0"
+ android:valueFrom="@color/biometric_face_icon_gray"
+ android:valueTo="?android:attr/colorAccent"
+ android:valueType="colorType">
+ <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_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="strokeColor"
+ android:startOffset="0"
+ android:valueFrom="@color/biometric_face_icon_gray"
+ android:valueTo="?android:attr/colorAccent"
+ android:valueType="colorType">
+ <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_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="@color/biometric_face_icon_gray"
+ android:valueTo="?android:attr/colorAccent"
+ android:valueType="colorType">
+ <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_D_3_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="@color/biometric_face_icon_gray"
+ android:valueTo="?android:attr/colorAccent"
+ android:valueType="colorType">
+ <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_D_4_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="@color/biometric_face_icon_gray"
+ android:valueTo="?android:attr/colorAccent"
+ android:valueType="colorType">
+ <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="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_face_to_error.xml b/packages/SystemUI/res/drawable/face_dialog_face_to_error.xml
new file mode 100644
index 0000000..a96d21a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/face_dialog_face_to_error.xml
@@ -0,0 +1,473 @@
+<?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
+ -->
+
+<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:width="60dp"
+ android:height="60dp"
+ android:viewportWidth="60"
+ android:viewportHeight="60">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G_N_1_T_0"
+ android:translateX="30"
+ android:translateY="30">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="-30"
+ android:translateY="-30">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
+ android:strokeWidth="2.5"
+ android:strokeAlpha="1"
+ android:strokeColor="@color/biometric_face_icon_gray"
+ android:trimPathStart="0"
+ android:trimPathEnd="1"
+ android:trimPathOffset="0" />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:pathData=" M33.75 42.75 C32.75,43.76 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
+ android:strokeWidth="2"
+ android:strokeAlpha="1"
+ android:strokeColor="@color/biometric_face_icon_gray"
+ android:trimPathStart="0"
+ android:trimPathEnd="1"
+ android:trimPathOffset="0" />
+ <group
+ android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0"
+ android:scaleX="1"
+ android:scaleY="1"
+ android:translateX="41.1"
+ android:translateY="23.8">
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="1"
+ android:fillColor="@color/biometric_face_icon_gray"
+ android:fillType="nonZero"
+ android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.1,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0"
+ android:scaleX="1"
+ android:scaleY="1"
+ android:translateX="18.6"
+ android:translateY="23.8">
+ <path
+ android:name="_R_G_L_0_G_D_3_P_0"
+ android:fillAlpha="1"
+ android:fillColor="@color/biometric_face_icon_gray"
+ android:fillType="nonZero"
+ android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.2,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0"
+ android:translateX="30.3"
+ android:translateY="31.45">
+ <path
+ android:name="_R_G_L_0_G_D_4_P_0"
+ android:fillAlpha="1"
+ android:fillColor="@color/biometric_face_icon_gray"
+ android:fillType="nonZero"
+ android:pathData=" M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c " />
+ </group>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="strokeColor"
+ android:startOffset="0"
+ android:valueFrom="@color/biometric_face_icon_gray"
+ android:valueTo="@color/biometric_face_icon_gray"
+ android:valueType="colorType">
+ <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:duration="17"
+ android:propertyName="strokeColor"
+ android:startOffset="50"
+ android:valueFrom="@color/biometric_face_icon_gray"
+ android:valueTo="?android:attr/colorError"
+ android:valueType="colorType">
+ <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_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="strokeColor"
+ android:startOffset="0"
+ android:valueFrom="@color/biometric_face_icon_gray"
+ android:valueTo="@color/biometric_face_icon_gray"
+ android:valueType="colorType">
+ <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:duration="17"
+ android:propertyName="strokeColor"
+ android:startOffset="50"
+ android:valueFrom="@color/biometric_face_icon_gray"
+ android:valueTo="?android:attr/colorError"
+ android:valueType="colorType">
+ <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_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="217"
+ android:propertyName="strokeWidth"
+ android:startOffset="0"
+ android:valueFrom="2"
+ android:valueTo="2.5"
+ 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_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="217"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M33.75 42.75 C32.75,43.76 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
+ android:valueTo="M34.78 38.76 C33.83,38.75 31.54,38.75 30.01,38.75 C26.97,38.75 26.14,38.75 24.3,38.76 "
+ android:valueType="pathType">
+ <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_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="217"
+ android:propertyName="trimPathStart"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0.34"
+ 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_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="217"
+ android:propertyName="trimPathEnd"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0.5700000000000001"
+ 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_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <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:duration="17"
+ android:propertyName="fillAlpha"
+ android:startOffset="50"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <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_D_2_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="67"
+ android:pathData="M 41.1,23.8C 40.547981774806985,23.08834635019302 38.34001822519301,20.24165364980698 37.788,19.53"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="0">
+ <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_2_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0.3"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0.3"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_3_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <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:duration="17"
+ android:propertyName="fillAlpha"
+ android:startOffset="50"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <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_D_3_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="67"
+ android:pathData="M 18.6,23.8C 19.16757813692093,23.08502601385117 21.43742186307907,20.224973986148832 22.005,19.51"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="0">
+ <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_3_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0.3"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0.3"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_4_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="@color/biometric_face_icon_gray"
+ android:valueTo="@color/biometric_face_icon_gray"
+ android:valueType="colorType">
+ <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:duration="17"
+ android:propertyName="fillColor"
+ android:startOffset="50"
+ android:valueFrom="@color/biometric_face_icon_gray"
+ android:valueTo="?android:attr/colorError"
+ android:valueType="colorType">
+ <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_D_4_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="67"
+ android:pathData="M 30.3,31.45C 30.3,31.07740886211395 30.3,31.82259113788605 30.3,31.45"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="0">
+ <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>
+ <objectAnimator
+ android:duration="150"
+ android:pathData="M 30.3,31.45C 30.3,31.07740886211395 30.3,29.58759113788605 30.3,29.215"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="67">
+ <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_4_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c "
+ android:valueTo="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-3.25 -1.5,-3.25 C-1.5,-3.25 0.9,-3.25 0.9,-3.25 C0.9,-3.25 0.9,3.25 0.9,3.25c "
+ android:valueType="pathType">
+ <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>
+ <objectAnimator
+ android:duration="150"
+ android:propertyName="pathData"
+ android:startOffset="67"
+ android:valueFrom="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-3.25 -1.5,-3.25 C-1.5,-3.25 0.9,-3.25 0.9,-3.25 C0.9,-3.25 0.9,3.25 0.9,3.25c "
+ android:valueTo="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-13.15 -1.5,-13.15 C-1.5,-13.15 0.9,-13.15 0.9,-13.15 C0.9,-13.15 0.9,3.25 0.9,3.25c "
+ android:valueType="pathType">
+ <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>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="pathData"
+ android:startOffset="217"
+ android:valueFrom="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-13.15 -1.5,-13.15 C-1.5,-13.15 0.9,-13.15 0.9,-13.15 C0.9,-13.15 0.9,3.25 0.9,3.25c "
+ android:valueTo="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-11.71 -1.5,-11.71 C-1.5,-11.71 0.9,-11.71 0.9,-11.71 C0.9,-11.71 0.9,3.25 0.9,3.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,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:duration="333"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_icon.xml b/packages/SystemUI/res/drawable/face_dialog_icon.xml
deleted file mode 100644
index 6d28b5a..0000000
--- a/packages/SystemUI/res/drawable/face_dialog_icon.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp"
- android:width="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path android:fillColor="#000" android:pathData="M9,11.75A1.25,1.25 0 0,0 7.75,13A1.25,1.25 0 0,0 9,14.25A1.25,1.25 0 0,0 10.25,13A1.25,1.25 0 0,0 9,11.75M15,11.75A1.25,1.25 0 0,0 13.75,13A1.25,1.25 0 0,0 15,14.25A1.25,1.25 0 0,0 16.25,13A1.25,1.25 0 0,0 15,11.75M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,11.71 4,11.42 4.05,11.14C6.41,10.09 8.28,8.16 9.26,5.77C11.07,8.33 14.05,10 17.42,10C18.2,10 18.95,9.91 19.67,9.74C19.88,10.45 20,11.21 20,12C20,16.41 16.41,20 12,20Z" />
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
index af25e44..8f411f4 100644
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
@@ -18,336 +18,268 @@
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24"
- android:viewportWidth="24">
+ android:width="60dp"
+ android:height="60dp"
+ android:viewportHeight="60"
+ android:viewportWidth="60">
<group android:name="_R_G">
<group
- android:name="_R_G_L_2_G"
- android:pivotX="-33"
- android:pivotY="-34"
- android:rotation="180"
- android:scaleX="0.738"
- android:scaleY="0.738"
- android:translateX="45"
- android:translateY="46.4">
- <path
- android:name="_R_G_L_2_G_D_0_P_0"
- android:pathData=" M-25.36 -24.41 C-25.93,-24.31 -26.49,-24.27 -26.81,-24.27 C-28.11,-24.27 -29.35,-24.62 -30.43,-25.4 C-32.11,-26.6 -33.2,-28.57 -33.2,-30.79 "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorAccent"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="1.45"
- android:trimPathEnd="0"
- android:trimPathOffset="0"
- android:trimPathStart="0" />
- <path
- android:name="_R_G_L_2_G_D_1_P_0"
- android:pathData=" M-36.14 -21.78 C-37.15,-22.98 -37.72,-23.7 -38.51,-25.29 C-39.33,-26.94 -39.82,-28.78 -39.82,-30.77 C-39.82,-34.43 -36.85,-37.4 -33.19,-37.4 C-29.52,-37.4 -26.55,-34.43 -26.55,-30.77 "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorAccent"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="1.45"
- android:trimPathEnd="0"
- android:trimPathOffset="0"
- android:trimPathStart="0" />
- <path
- android:name="_R_G_L_2_G_D_2_P_0"
- android:pathData=" M-42.19 -25.68 C-42.95,-27.82 -43.09,-29.54 -43.09,-30.8 C-43.09,-32.27 -42.84,-33.65 -42.27,-34.9 C-40.71,-38.35 -37.24,-40.75 -33.2,-40.75 C-27.71,-40.75 -23.26,-36.3 -23.26,-30.8 C-23.26,-28.97 -24.74,-27.49 -26.57,-27.49 C-28.4,-27.49 -29.89,-28.97 -29.89,-30.8 C-29.89,-32.64 -31.37,-34.12 -33.2,-34.12 C-35.04,-34.12 -36.52,-32.64 -36.52,-30.8 C-36.52,-28.23 -35.53,-25.92 -33.92,-24.22 C-32.69,-22.93 -31.48,-22.12 -29.44,-21.53 "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorAccent"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="1.45"
- android:trimPathEnd="1"
- android:trimPathOffset="0"
- android:trimPathStart="1" />
- <path
- android:name="_R_G_L_2_G_D_3_P_0"
- android:pathData=" M-44.06 -38.17 C-42.87,-39.94 -41.39,-41.41 -39.51,-42.44 C-37.62,-43.47 -35.46,-44.05 -33.16,-44.05 C-30.88,-44.05 -28.72,-43.47 -26.85,-42.45 C-24.97,-41.43 -23.48,-39.97 -22.29,-38.21 "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorAccent"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="1.45"
- android:trimPathEnd="1"
- android:trimPathOffset="0"
- android:trimPathStart="1" />
- <path
- android:name="_R_G_L_2_G_D_4_P_0"
- android:pathData=" M-25.72 -45.45 C-27.99,-46.76 -30.43,-47.52 -33.28,-47.52 C-36.13,-47.52 -38.51,-46.74 -40.62,-45.45 "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorAccent"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="1.45"
- android:trimPathEnd="0"
- android:trimPathOffset="0"
- android:trimPathStart="0" />
- </group>
- <group
- android:name="_R_G_L_1_G"
- android:rotation="10"
- android:translateX="12"
- android:translateY="12">
- <path
- android:name="_R_G_L_1_G_D_0_P_0"
- android:pathData=" M0 -9 C4.97,-9 9,-4.97 9,0 C9,4.97 4.97,9 0,9 C-4.97,9 -9,4.97 -9,0 C-9,-4.97 -4.97,-9 0,-9c "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorError"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="2"
- android:trimPathEnd="1"
- android:trimPathOffset="0"
- android:trimPathStart="0" />
- </group>
- <group
- android:name="_R_G_L_0_G"
- android:translateX="12"
- android:translateY="12">
+ android:name="_R_G_L_1_G_N_4_T_0"
+ android:translateX="30"
+ android:translateY="30">
<group
- android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
- android:pivotY="-0.012"
- android:rotation="0"
- android:scaleX="1"
- android:scaleY="1">
+ android:name="_R_G_L_1_G"
+ android:pivotX="114"
+ android:pivotY="114"
+ android:scaleX="0.42200000000000004"
+ android:scaleY="0.42200000000000004"
+ android:translateX="-114"
+ android:translateY="-114">
<path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:fillAlpha="1"
- android:fillColor="?android:attr/colorError"
- android:fillType="nonZero"
- android:pathData=" M1.1 3.94 C1.1,4.55 0.61,5.04 0,5.04 C-0.61,5.04 -1.1,4.55 -1.1,3.94 C-1.1,3.33 -0.61,2.84 0,2.84 C0.61,2.84 1.1,3.33 1.1,3.94c " />
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:pathData=" M79.63 67.24 C79.63,67.24 111.5,47.42 147.83,67.24 "
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorAccent"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="5.5"
+ android:trimPathEnd="0"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
+ <path
+ android:name="_R_G_L_1_G_D_1_P_0"
+ android:pathData=" M64.27 98.07 C64.27,98.07 80.13,73.02 113.98,73.02 C147.83,73.02 163.56,97.26 163.56,97.26 "
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorAccent"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="5.5"
+ android:trimPathEnd="0"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
+ <path
+ android:name="_R_G_L_1_G_D_2_P_0"
+ android:pathData=" M72.53 151.07 C72.53,151.07 62.46,122.89 76.16,105.55 C89.86,88.21 106.72,86.73 113.98,86.73 C121.08,86.73 153.51,90.62 158.7,125.87 C159.14,128.82 158.8,132.88 157.18,136.09 C154.88,140.63 150.62,143.63 145.85,143.97 C133.78,144.85 129.76,137.92 129.26,128.49 C128.88,121.19 122.49,115.35 113.15,115.35 C102.91,115.35 95.97,126.69 99.77,139.74 C103.57,152.78 111.33,163.85 130.32,169.13 "
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorAccent"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="5.5"
+ android:trimPathEnd="0"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
+ <path
+ android:name="_R_G_L_1_G_D_3_P_0"
+ android:pathData=" M100.6 167.84 C100.6,167.84 82.76,152.1 83.75,130.31 C84.75,108.53 102.58,100.7 113.73,100.7 C124.87,100.7 144.19,108.56 144.19,130.01 "
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorAccent"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="5.5"
+ android:trimPathEnd="0"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
+ <path
+ android:name="_R_G_L_1_G_D_4_P_0"
+ android:pathData=" M113.73 129.17 C113.73,129.17 113.15,161.33 149.15,156.58 "
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorAccent"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="5.5"
+ android:trimPathEnd="0"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
</group>
+ </group>
+ <group
+ android:name="_R_G_L_0_G_N_4_T_0"
+ android:translateX="30"
+ android:translateY="30">
<group
- android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0"
- android:pivotY="-0.012"
- android:rotation="0"
- android:scaleX="1"
- android:scaleY="1">
+ android:name="_R_G_L_0_G"
+ android:translateX="-30.05"
+ android:translateY="-30">
+ <group
+ android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+ android:scaleX="1"
+ android:scaleY="1"
+ android:translateX="30"
+ android:translateY="38.75">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="?android:attr/colorError"
+ android:fillType="nonZero"
+ android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0"
+ android:pivotX="0.002"
+ android:pivotY="7.488"
+ android:scaleX="1"
+ android:scaleY="1"
+ android:translateX="30"
+ android:translateY="25">
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="?android:attr/colorError"
+ android:fillType="nonZero"
+ android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c " />
+ </group>
<path
- android:name="_R_G_L_0_G_D_0_P_1"
- android:fillAlpha="1"
- android:fillColor="?android:attr/colorError"
- android:fillType="nonZero"
- android:pathData=" M1 -4.06 C1,-4.06 1,-0.06 1,-0.06 C1,0.49 0.55,0.94 0,0.94 C-0.55,0.94 -1,0.49 -1,-0.06 C-1,-0.06 -1,-4.06 -1,-4.06 C-1,-4.61 -0.55,-5.06 0,-5.06 C0.55,-5.06 1,-4.61 1,-4.06c " />
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorError"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2.5"
+ android:trimPathEnd="1"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
</group>
</group>
</group>
<group android:name="time_group" />
</vector>
</aapt:attr>
- <target android:name="_R_G_L_2_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="350"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- 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>
- <objectAnimator
- android:duration="350"
- android:propertyName="trimPathEnd"
- android:startOffset="350"
- android:valueFrom="0"
- android:valueTo="1"
- 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_2_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="267"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- 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>
- <objectAnimator
- android:duration="433"
- android:propertyName="trimPathEnd"
- android:startOffset="267"
- android:valueFrom="0"
- android:valueTo="1"
- 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_2_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="250"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1"
- 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>
- <objectAnimator
- android:duration="250"
- android:propertyName="trimPathStart"
- android:startOffset="250"
- android:valueFrom="1"
- android:valueTo="0"
- 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_2_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="350"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1"
- 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>
- <objectAnimator
- android:duration="117"
- android:propertyName="trimPathStart"
- android:startOffset="350"
- android:valueFrom="1"
- android:valueTo="0"
- 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_2_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="417"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- 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>
- <objectAnimator
- android:duration="117"
- android:propertyName="trimPathEnd"
- android:startOffset="417"
- android:valueFrom="0"
- android:valueTo="1"
- 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_2_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="200"
- android:propertyName="rotation"
- android:startOffset="0"
- android:valueFrom="180"
- android:valueTo="180"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="rotation"
- android:startOffset="200"
- android:valueFrom="180"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
<target android:name="_R_G_L_1_G_D_0_P_0">
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
- android:duration="383"
+ android:duration="83"
android:propertyName="trimPathEnd"
android:startOffset="0"
- android:valueFrom="1"
+ android:valueFrom="0"
android:valueTo="0"
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>
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="trimPathEnd"
+ android:startOffset="83"
+ android:valueFrom="0"
+ android:valueTo="1"
+ 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_1_G">
+ <target android:name="_R_G_L_1_G_D_1_P_0">
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
- android:duration="33"
- android:propertyName="rotation"
+ android:duration="83"
+ android:propertyName="trimPathEnd"
android:startOffset="0"
- android:valueFrom="10"
- android:valueTo="10"
+ android:valueFrom="0"
+ android:valueTo="0"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
<objectAnimator
- android:duration="367"
- android:propertyName="rotation"
- android:startOffset="33"
- android:valueFrom="10"
- android:valueTo="-180"
+ android:duration="250"
+ android:propertyName="trimPathEnd"
+ android:startOffset="83"
+ android:valueFrom="0"
+ android:valueTo="1"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+ <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_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="trimPathEnd"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ 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>
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="trimPathEnd"
+ android:startOffset="83"
+ android:valueFrom="0"
+ android:valueTo="1"
+ 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_1_G_D_3_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="trimPathEnd"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ 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>
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="trimPathEnd"
+ android:startOffset="83"
+ android:valueFrom="0"
+ android:valueTo="1"
+ 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_1_G_D_4_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="trimPathEnd"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ 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>
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="trimPathEnd"
+ android:startOffset="83"
+ android:valueFrom="0"
+ android:valueTo="1"
+ 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>
@@ -357,153 +289,125 @@
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
- android:duration="17"
- android:propertyName="rotation"
+ android:duration="67"
+ android:propertyName="scaleX"
android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
+ android:valueFrom="1"
+ android:valueTo="1.1"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.2,1 1.0,1.0" />
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
<objectAnimator
- android:duration="417"
- android:propertyName="rotation"
- android:startOffset="17"
- android:valueFrom="0"
- android:valueTo="-180"
+ android:duration="67"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="1.1"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.2,1 1.0,1.0" />
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleX"
+ android:startOffset="67"
+ android:valueFrom="1.1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleY"
+ android:startOffset="67"
+ android:valueFrom="1.1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0" />
</aapt:attr>
</objectAnimator>
</set>
</aapt:attr>
</target>
- <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+ <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
- android:duration="117"
+ android:duration="67"
android:propertyName="scaleX"
android:startOffset="0"
android:valueFrom="1"
android:valueTo="1"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.6,1 1.0,1.0" />
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.659,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
<objectAnimator
- android:duration="117"
+ android:duration="67"
android:propertyName="scaleY"
android:startOffset="0"
android:valueFrom="1"
+ android:valueTo="1.1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleX"
+ android:startOffset="67"
+ android:valueFrom="1"
android:valueTo="1"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.6,1 1.0,1.0" />
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
<objectAnimator
- android:duration="317"
- android:propertyName="scaleX"
- android:startOffset="117"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
+ android:duration="100"
android:propertyName="scaleY"
- android:startOffset="117"
- android:valueFrom="1"
+ android:startOffset="67"
+ android:valueFrom="1.1"
android:valueTo="0"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.6,1 1.0,1.0" />
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.096 1.0,1.0" />
</aapt:attr>
</objectAnimator>
</set>
</aapt:attr>
</target>
- <target android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0">
+ <target android:name="_R_G_L_0_G_D_2_P_0">
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
- android:duration="17"
- android:propertyName="rotation"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="417"
- android:propertyName="rotation"
- android:startOffset="17"
- android:valueFrom="0"
- android:valueTo="-180"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.8,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_1_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="117"
- android:propertyName="scaleX"
+ android:duration="67"
+ android:propertyName="trimPathEnd"
android:startOffset="0"
android:valueFrom="1"
android:valueTo="1"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.6,1 1.0,1.0" />
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
<objectAnimator
- android:duration="117"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="scaleX"
- android:startOffset="117"
+ android:duration="133"
+ android:propertyName="trimPathEnd"
+ android:startOffset="67"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="scaleY"
- android:startOffset="117"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.6,1 1.0,1.0" />
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
</set>
@@ -513,7 +417,7 @@
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
- android:duration="717"
+ android:duration="350"
android:propertyName="translateX"
android:startOffset="0"
android:valueFrom="0"
@@ -522,4 +426,4 @@
</set>
</aapt:attr>
</target>
-</animated-vector>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
index 1a7a846..89b8228 100644
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
@@ -18,614 +18,133 @@
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24"
- android:viewportWidth="24">
+ android:width="60dp"
+ android:height="60dp"
+ android:viewportHeight="60"
+ android:viewportWidth="60">
<group android:name="_R_G">
<group
- android:name="_R_G_L_3_G"
- android:pivotX="-33"
- android:pivotY="-34"
- android:rotation="0"
- android:scaleX="0.738"
- android:scaleY="0.738"
- android:translateX="45"
- android:translateY="46.4">
- <path
- android:name="_R_G_L_3_G_D_0_P_0"
- android:pathData=" M-25.36 -24.41 C-25.93,-24.31 -26.49,-24.27 -26.81,-24.27 C-28.11,-24.27 -29.35,-24.62 -30.43,-25.4 C-32.11,-26.6 -33.2,-28.57 -33.2,-30.79 "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorAccent"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="1.45"
- android:trimPathEnd="1"
- android:trimPathOffset="0"
- android:trimPathStart="0" />
- <path
- android:name="_R_G_L_3_G_D_1_P_0"
- android:pathData=" M-36.14 -21.78 C-37.15,-22.98 -37.72,-23.7 -38.51,-25.29 C-39.33,-26.94 -39.82,-28.78 -39.82,-30.77 C-39.82,-34.43 -36.85,-37.4 -33.19,-37.4 C-29.52,-37.4 -26.55,-34.43 -26.55,-30.77 "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorAccent"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="1.45"
- android:trimPathEnd="1"
- android:trimPathOffset="0"
- android:trimPathStart="0" />
- <path
- android:name="_R_G_L_3_G_D_2_P_0"
- android:pathData=" M-42.19 -25.68 C-42.95,-27.82 -43.09,-29.54 -43.09,-30.8 C-43.09,-32.27 -42.84,-33.65 -42.27,-34.9 C-40.71,-38.35 -37.24,-40.75 -33.2,-40.75 C-27.71,-40.75 -23.26,-36.3 -23.26,-30.8 C-23.26,-28.97 -24.74,-27.49 -26.57,-27.49 C-28.4,-27.49 -29.89,-28.97 -29.89,-30.8 C-29.89,-32.64 -31.37,-34.12 -33.2,-34.12 C-35.04,-34.12 -36.52,-32.64 -36.52,-30.8 C-36.52,-28.23 -35.53,-25.92 -33.92,-24.22 C-32.69,-22.93 -31.48,-22.12 -29.44,-21.53 "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorAccent"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="1.45"
- android:trimPathEnd="1"
- android:trimPathOffset="0"
- android:trimPathStart="0" />
- <path
- android:name="_R_G_L_3_G_D_3_P_0"
- android:pathData=" M-44.06 -38.17 C-42.87,-39.94 -41.39,-41.41 -39.51,-42.44 C-37.62,-43.47 -35.46,-44.05 -33.16,-44.05 C-30.88,-44.05 -28.72,-43.47 -26.85,-42.45 C-24.97,-41.43 -23.48,-39.97 -22.29,-38.21 "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorAccent"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="1.45"
- android:trimPathEnd="1"
- android:trimPathOffset="0"
- android:trimPathStart="0" />
- <path
- android:name="_R_G_L_3_G_D_4_P_0"
- android:pathData=" M-25.72 -45.45 C-27.99,-46.76 -30.43,-47.52 -33.28,-47.52 C-36.13,-47.52 -38.51,-46.74 -40.62,-45.45 "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorAccent"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="1.45"
- android:trimPathEnd="1"
- android:trimPathOffset="0"
- android:trimPathStart="0" />
- </group>
- <group
- android:name="_R_G_L_2_G"
- android:pivotX="-33"
- android:pivotY="-34"
- android:rotation="0"
- android:scaleX="0.738"
- android:scaleY="0.738"
- android:translateX="45"
- android:translateY="46.4">
- <path
- android:name="_R_G_L_2_G_D_0_P_0"
- android:pathData=" M-25.36 -24.41 C-25.93,-24.31 -26.49,-24.27 -26.81,-24.27 C-28.11,-24.27 -29.35,-24.62 -30.43,-25.4 C-32.11,-26.6 -33.2,-28.57 -33.2,-30.79 "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorError"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="1.45"
- android:trimPathEnd="0"
- android:trimPathOffset="0"
- android:trimPathStart="0" />
- <path
- android:name="_R_G_L_2_G_D_1_P_0"
- android:pathData=" M-36.14 -21.78 C-37.15,-22.98 -37.72,-23.7 -38.51,-25.29 C-39.33,-26.94 -39.82,-28.78 -39.82,-30.77 C-39.82,-34.43 -36.85,-37.4 -33.19,-37.4 C-29.52,-37.4 -26.55,-34.43 -26.55,-30.77 "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorError"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="1.45"
- android:trimPathEnd="0"
- android:trimPathOffset="0"
- android:trimPathStart="0" />
- <path
- android:name="_R_G_L_2_G_D_2_P_0"
- android:pathData=" M-42.19 -25.68 C-42.95,-27.82 -43.09,-29.54 -43.09,-30.8 C-43.09,-32.27 -42.84,-33.65 -42.27,-34.9 C-40.71,-38.35 -37.24,-40.75 -33.2,-40.75 C-27.71,-40.75 -23.26,-36.3 -23.26,-30.8 C-23.26,-28.97 -24.74,-27.49 -26.57,-27.49 C-28.4,-27.49 -29.89,-28.97 -29.89,-30.8 C-29.89,-32.64 -31.37,-34.12 -33.2,-34.12 C-35.04,-34.12 -36.52,-32.64 -36.52,-30.8 C-36.52,-28.23 -35.53,-25.92 -33.92,-24.22 C-32.69,-22.93 -31.48,-22.12 -29.44,-21.53 "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorError"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="1.45"
- android:trimPathEnd="0"
- android:trimPathOffset="0"
- android:trimPathStart="0" />
- <path
- android:name="_R_G_L_2_G_D_3_P_0"
- android:pathData=" M-44.06 -38.17 C-42.87,-39.94 -41.39,-41.41 -39.51,-42.44 C-37.62,-43.47 -35.46,-44.05 -33.16,-44.05 C-30.88,-44.05 -28.72,-43.47 -26.85,-42.45 C-24.97,-41.43 -23.48,-39.97 -22.29,-38.21 "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorError"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="1.45"
- android:trimPathEnd="1"
- android:trimPathOffset="0"
- android:trimPathStart="1" />
- <path
- android:name="_R_G_L_2_G_D_4_P_0"
- android:pathData=" M-25.72 -45.45 C-27.99,-46.76 -30.43,-47.52 -33.28,-47.52 C-36.13,-47.52 -38.51,-46.74 -40.62,-45.45 "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorError"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="1.45"
- android:trimPathEnd="0"
- android:trimPathOffset="0"
- android:trimPathStart="0" />
- </group>
- <group
- android:name="_R_G_L_1_G"
- android:rotation="190"
- android:translateX="12"
- android:translateY="12">
- <path
- android:name="_R_G_L_1_G_D_0_P_0"
- android:pathData=" M0 -9 C4.97,-9 9,-4.97 9,0 C9,4.97 4.97,9 0,9 C-4.97,9 -9,4.97 -9,0 C-9,-4.97 -4.97,-9 0,-9c "
- android:strokeAlpha="1"
- android:strokeColor="?android:attr/colorError"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="2"
- android:trimPathEnd="1"
- android:trimPathOffset="0"
- android:trimPathStart="1" />
- </group>
- <group
- android:name="_R_G_L_0_G"
- android:translateX="12"
- android:translateY="12">
+ android:name="_R_G_L_1_G_N_4_T_0"
+ android:translateX="30"
+ android:translateY="30">
<group
- android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
- android:pivotY="-0.012"
- android:rotation="184"
- android:scaleX="0"
- android:scaleY="0">
+ android:name="_R_G_L_1_G"
+ android:pivotX="114"
+ android:pivotY="114"
+ android:scaleX="0.42244"
+ android:scaleY="0.42244"
+ android:translateX="-114"
+ android:translateY="-114">
<path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:fillAlpha="1"
- android:fillColor="?android:attr/colorError"
- android:fillType="nonZero"
- android:pathData=" M1.1 3.94 C1.1,4.55 0.61,5.04 0,5.04 C-0.61,5.04 -1.1,4.55 -1.1,3.94 C-1.1,3.33 -0.61,2.84 0,2.84 C0.61,2.84 1.1,3.33 1.1,3.94c " />
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:pathData=" M79.63 67.24 C79.63,67.24 111.5,47.42 147.83,67.24 "
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorAccent"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="5.5"
+ android:trimPathEnd="1"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
+ <path
+ android:name="_R_G_L_1_G_D_1_P_0"
+ android:pathData=" M64.27 98.07 C64.27,98.07 80.13,73.02 113.98,73.02 C147.83,73.02 163.56,97.26 163.56,97.26 "
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorAccent"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="5.5"
+ android:trimPathEnd="1"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
+ <path
+ android:name="_R_G_L_1_G_D_2_P_0"
+ android:pathData=" M72.53 151.07 C72.53,151.07 62.46,122.89 76.16,105.55 C89.86,88.21 106.72,86.73 113.98,86.73 C121.08,86.73 153.51,90.62 158.7,125.87 C159.14,128.82 158.8,132.88 157.18,136.09 C154.88,140.63 150.62,143.63 145.85,143.97 C133.78,144.85 129.76,137.92 129.26,128.49 C128.88,121.19 122.49,115.35 113.15,115.35 C102.91,115.35 95.97,126.69 99.77,139.74 C103.57,152.78 111.33,163.85 130.32,169.13 "
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorAccent"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="5.5"
+ android:trimPathEnd="1"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
+ <path
+ android:name="_R_G_L_1_G_D_3_P_0"
+ android:pathData=" M100.6 167.84 C100.6,167.84 82.76,152.1 83.75,130.31 C84.75,108.53 102.58,100.7 113.73,100.7 C124.87,100.7 144.19,108.56 144.19,130.01 "
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorAccent"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="5.5"
+ android:trimPathEnd="1"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
+ <path
+ android:name="_R_G_L_1_G_D_4_P_0"
+ android:pathData=" M113.73 129.17 C113.73,129.17 113.15,161.33 149.15,156.58 "
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorAccent"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="5.5"
+ android:trimPathEnd="1"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
</group>
+ </group>
+ <group
+ android:name="_R_G_L_0_G_N_4_T_0"
+ android:translateX="30"
+ android:translateY="30">
<group
- android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0"
- android:pivotY="-0.012"
- android:rotation="184"
- android:scaleX="0"
- android:scaleY="0">
+ android:name="_R_G_L_0_G"
+ android:translateX="-30.05"
+ android:translateY="-30">
+ <group
+ android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+ android:scaleX="0"
+ android:scaleY="0"
+ android:translateX="30"
+ android:translateY="38.75">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="?android:attr/colorError"
+ android:fillType="nonZero"
+ android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0"
+ android:pivotX="0.002"
+ android:pivotY="7.488"
+ android:scaleX="1"
+ android:scaleY="0"
+ android:translateX="30"
+ android:translateY="25">
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="?android:attr/colorError"
+ android:fillType="nonZero"
+ android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c " />
+ </group>
<path
- android:name="_R_G_L_0_G_D_0_P_1"
- android:fillAlpha="1"
- android:fillColor="?android:attr/colorError"
- android:fillType="nonZero"
- android:pathData=" M1 -4.06 C1,-4.06 1,-0.06 1,-0.06 C1,0.49 0.55,0.94 0,0.94 C-0.55,0.94 -1,0.49 -1,-0.06 C-1,-0.06 -1,-4.06 -1,-4.06 C-1,-4.61 -0.55,-5.06 0,-5.06 C0.55,-5.06 1,-4.61 1,-4.06c " />
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/colorError"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2.5"
+ android:trimPathEnd="1"
+ android:trimPathOffset="0"
+ android:trimPathStart="1" />
</group>
</group>
</group>
<group android:name="time_group" />
</vector>
</aapt:attr>
- <target android:name="_R_G_L_3_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="150"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <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_3_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="33"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <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:duration="150"
- android:propertyName="trimPathStart"
- android:startOffset="33"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <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_3_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="17"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <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:duration="217"
- android:propertyName="trimPathStart"
- android:startOffset="17"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <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_3_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="17"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1"
- android:valueType="floatType">
- <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:duration="133"
- android:propertyName="trimPathEnd"
- android:startOffset="17"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <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_3_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="17"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <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:duration="67"
- android:propertyName="trimPathStart"
- android:startOffset="17"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <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_3_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="100"
- android:propertyName="rotation"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="567"
- android:propertyName="rotation"
- android:startOffset="100"
- android:valueFrom="0"
- android:valueTo="-305"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_2_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="150"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.5,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="167"
- android:propertyName="trimPathStart"
- android:startOffset="150"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.5,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_2_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="167"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- 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_2_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="133"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.5,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="200"
- android:propertyName="trimPathStart"
- android:startOffset="133"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.5,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_2_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="17"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- 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>
- <objectAnimator
- android:duration="217"
- android:propertyName="trimPathEnd"
- android:startOffset="17"
- android:valueFrom="0"
- android:valueTo="1"
- 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_2_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="133"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="200"
- android:propertyName="trimPathStart"
- android:startOffset="133"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_2_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="250"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_2_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="167"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_2_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="117"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="117"
- android:propertyName="trimPathEnd"
- android:startOffset="117"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_2_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="100"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.5,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="100"
- android:propertyName="trimPathStart"
- android:startOffset="100"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.5,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_2_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="133"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- 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_2_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="100"
- android:propertyName="rotation"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="567"
- android:propertyName="rotation"
- android:startOffset="100"
- android:valueFrom="0"
- android:valueTo="-305"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
<target android:name="_R_G_L_1_G_D_0_P_0">
<aapt:attr name="android:animation">
<set android:ordering="together">
@@ -633,6 +152,163 @@
android:duration="167"
android:propertyName="trimPathStart"
android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="trimPathStart"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="trimPathStart"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_3_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="trimPathStart"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_4_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="trimPathStart"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleX"
+ android:startOffset="167"
+ android:valueFrom="0"
+ android:valueTo="1.1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleY"
+ android:startOffset="167"
+ android:valueFrom="0"
+ android:valueTo="1.1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleX"
+ android:startOffset="267"
+ android:valueFrom="1.1"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleY"
+ android:startOffset="267"
+ android:valueFrom="1.1"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="scaleX"
+ android:startOffset="0"
android:valueFrom="1"
android:valueTo="1"
android:valueType="floatType">
@@ -641,11 +317,55 @@
</aapt:attr>
</objectAnimator>
<objectAnimator
- android:duration="533"
- android:propertyName="trimPathStart"
+ android:duration="167"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleX"
android:startOffset="167"
android:valueFrom="1"
- android:valueTo="0"
+ android:valueTo="1"
+ 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>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleY"
+ android:startOffset="167"
+ android:valueFrom="0"
+ android:valueTo="1.1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleX"
+ android:startOffset="267"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.341,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleY"
+ android:startOffset="267"
+ android:valueFrom="1.1"
+ android:valueTo="1"
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" />
@@ -654,185 +374,18 @@
</set>
</aapt:attr>
</target>
- <target android:name="_R_G_L_1_G">
+ <target android:name="_R_G_L_0_G_D_2_P_0">
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
- android:duration="150"
- android:propertyName="rotation"
+ android:duration="267"
+ android:propertyName="trimPathStart"
android:startOffset="0"
- android:valueFrom="190"
- android:valueTo="190"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="550"
- android:propertyName="rotation"
- android:startOffset="150"
- android:valueFrom="190"
- android:valueTo="-6"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="283"
- android:propertyName="rotation"
- android:startOffset="0"
- android:valueFrom="184"
- android:valueTo="184"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="rotation"
- android:startOffset="283"
- android:valueFrom="184"
+ android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="283"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="283"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="scaleX"
- android:startOffset="283"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="scaleY"
- android:startOffset="283"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="283"
- android:propertyName="rotation"
- android:startOffset="0"
- android:valueFrom="184"
- android:valueTo="184"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="rotation"
- android:startOffset="283"
- android:valueFrom="184"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="283"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="283"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="scaleX"
- android:startOffset="283"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="scaleY"
- android:startOffset="283"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
</set>
@@ -842,7 +395,7 @@
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
- android:duration="717"
+ android:duration="350"
android:propertyName="translateX"
android:startOffset="0"
android:valueFrom="0"
@@ -851,4 +404,4 @@
</set>
</aapt:attr>
</target>
-</animated-vector>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index b3567f8..d5f29ba 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -130,6 +130,7 @@
<!-- Biometric dialog colors -->
<color name="biometric_dialog_dim_color">#80000000</color> <!-- 50% black -->
+ <color name="biometric_face_icon_gray">#ffbdc1c6</color>
<!-- Logout button -->
<color name="logout_button_bg_color">#ccffffff</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6037dfc..06df0e7 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -861,9 +861,6 @@
<dimen name="bottom_padding">48dp</dimen>
<dimen name="edge_margin">8dp</dimen>
- <dimen name="rounded_corner_radius">@*android:dimen/rounded_corner_radius</dimen>
- <dimen name="rounded_corner_radius_top">@*android:dimen/rounded_corner_radius_top</dimen>
- <dimen name="rounded_corner_radius_bottom">@*android:dimen/rounded_corner_radius_bottom</dimen>
<dimen name="rounded_corner_content_padding">0dp</dimen>
<dimen name="nav_content_padding">0dp</dimen>
<dimen name="nav_quick_scrub_track_edge_padding">24dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 874cdcc..6864ea1 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -48,14 +48,14 @@
import com.android.settingslib.Utils;
import com.android.settingslib.graph.BatteryMeterDrawableBase;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.policy.IconLogger;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 08fa434..a8d3763 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -38,6 +38,7 @@
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.power.EnhancedEstimates;
@@ -45,7 +46,7 @@
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.AmbientPulseManager;
-import com.android.systemui.statusbar.DisplayNavigationBarController;
+import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -64,6 +65,7 @@
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
@@ -79,7 +81,6 @@
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ExtensionController;
@@ -244,7 +245,7 @@
@Inject Lazy<NotificationRemoteInputManager.Callback> mNotificationRemoteInputManagerCallback;
@Inject Lazy<InitController> mInitController;
@Inject Lazy<AppOpsController> mAppOpsController;
- @Inject Lazy<DisplayNavigationBarController> mDisplayNavigationBarController;
+ @Inject Lazy<NavigationBarController> mNavigationBarController;
@Inject Lazy<StatusBarStateController> mStatusBarStateController;
@Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager;
@Inject Lazy<NotificationGroupAlertTransferHelper> mNotificationGroupAlertTransferHelper;
@@ -270,6 +271,7 @@
@Inject
Lazy<NotificationAlertingManager> mNotificationAlertingManager;
@Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
+ @Inject Lazy<AutoHideController> mAutoHideController;
@Inject @Named(BG_LOOPER_NAME) Lazy<Looper> mBgLooper;
@Inject @Named(BG_HANDLER_NAME) Lazy<Handler> mBgHandler;
@Inject @Named(MAIN_HANDLER_NAME) Lazy<Handler> mMainHandler;
@@ -410,8 +412,7 @@
mProviders.put(AppOpsController.class, mAppOpsController::get);
- mProviders.put(DisplayNavigationBarController.class,
- mDisplayNavigationBarController::get);
+ mProviders.put(NavigationBarController.class, mNavigationBarController::get);
mProviders.put(StatusBarStateController.class, mStatusBarStateController::get);
mProviders.put(NotificationLockscreenUserManager.class,
@@ -444,6 +445,12 @@
mProviders.put(NotificationEntryManager.class, mNotificationEntryManager::get);
mProviders.put(NotificationAlertingManager.class, mNotificationAlertingManager::get);
+ // TODO(b/118592525): to support multi-display , we start to add something which is
+ // per-display, while others may be global. I think it's time to add
+ // a new class maybe named DisplayDependency to solve per-display
+ // Dependency problem.
+ mProviders.put(AutoHideController.class, mAutoHideController::get);
+
sDependency = this;
}
@@ -512,6 +519,9 @@
public static void initDependencies(Context context) {
if (sDependency != null) return;
Dependency d = new Dependency();
+ SystemUIFactory.getInstance().getRootComponent()
+ .createDependency()
+ .createSystemUI(d);
d.mContext = context;
d.mComponents = new HashMap<>();
d.start();
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
index b93a5fd..1ee1dcf 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
@@ -19,6 +19,7 @@
import com.android.systemui.appops.AppOpsController;
import com.android.systemui.appops.AppOpsControllerImpl;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.power.PowerNotificationWarnings;
import com.android.systemui.power.PowerUI;
@@ -37,7 +38,6 @@
import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastControllerImpl;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
import com.android.systemui.statusbar.policy.ExtensionController;
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
index 2b521c5..dac977a 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -41,6 +41,8 @@
import com.android.systemui.plugins.PluginInitializerImpl;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.statusbar.NavigationBarController;
+import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -170,7 +172,21 @@
@Singleton
@Provides
+ public NavigationBarController provideNavigationBarController(Context context,
+ @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+ return new NavigationBarController(context, mainHandler);
+ }
+
+ @Singleton
+ @Provides
public ConfigurationController provideConfigurationController(Context context) {
return new ConfigurationControllerImpl(context);
}
+
+ @Singleton
+ @Provides
+ public AutoHideController provideAutoHideController(Context context,
+ @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+ return new AutoHideController(context, mainHandler);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 1dd231c..ab077d6 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -329,11 +329,11 @@
private void updateRoundedCornerRadii() {
final int newRoundedDefault = mContext.getResources().getDimensionPixelSize(
- R.dimen.rounded_corner_radius);
+ com.android.internal.R.dimen.rounded_corner_radius);
final int newRoundedDefaultTop = mContext.getResources().getDimensionPixelSize(
- R.dimen.rounded_corner_radius_top);
+ com.android.internal.R.dimen.rounded_corner_radius_top);
final int newRoundedDefaultBottom = mContext.getResources().getDimensionPixelSize(
- R.dimen.rounded_corner_radius_bottom);
+ com.android.internal.R.dimen.rounded_corner_radius_bottom);
final boolean roundedCornersChanged = mRoundedDefault != newRoundedDefault
|| mRoundedDefaultBottom != newRoundedDefaultBottom
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index ba89fe6..3167b9e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -240,7 +240,10 @@
mConfirmShowing = true;
mCurrentDialog.showConfirmationButton(true /* show */);
} else {
- handleHideDialog(false /* userCanceled */);
+ mCurrentDialog.updateState(BiometricDialogView.STATE_AUTHENTICATED);
+ mHandler.postDelayed(() -> {
+ handleHideDialog(false /* userCanceled */);
+ }, mCurrentDialog.getDelayAfterAuthenticatedDurationMs());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index e085f23..9934bfd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -21,6 +21,7 @@
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
+import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricPrompt;
import android.os.Binder;
@@ -63,7 +64,8 @@
protected static final int STATE_NONE = 0;
protected static final int STATE_AUTHENTICATING = 1;
protected static final int STATE_ERROR = 2;
- protected static final int STATE_AUTHENTICATED = 3;
+ protected static final int STATE_PENDING_CONFIRMATION = 3;
+ protected static final int STATE_AUTHENTICATED = 4;
private final IBinder mWindowToken = new Binder();
private final Interpolator mLinearOutSlowIn;
@@ -77,6 +79,8 @@
private final DialogViewCallback mCallback;
private ViewGroup mLayout;
+ private final Button mPositiveButton;
+ private final Button mNegativeButton;
private final TextView mErrorText;
private Bundle mBundle;
private final LinearLayout mDialog;
@@ -90,10 +94,12 @@
private boolean mPendingShowTryAgain;
private boolean mPendingShowConfirm;
- protected abstract void updateIcon(int lastState, int newState);
protected abstract int getHintStringResourceId();
protected abstract int getAuthenticatedAccessibilityResourceId();
protected abstract int getIconDescriptionResourceId();
+ protected abstract Drawable getAnimationForTransition(int oldState, int newState);
+ protected abstract boolean shouldAnimateForTransition(int oldState, int newState);
+ protected abstract int getDelayAfterAuthenticatedDurationMs();
private final Runnable mShowAnimationRunnable = new Runnable() {
@Override
@@ -178,10 +184,10 @@
final View space = mLayout.findViewById(R.id.space);
final View leftSpace = mLayout.findViewById(R.id.left_space);
final View rightSpace = mLayout.findViewById(R.id.right_space);
- final Button negative = mLayout.findViewById(R.id.button2);
- final Button positive = mLayout.findViewById(R.id.button1);
final ImageView icon = mLayout.findViewById(R.id.biometric_icon);
final Button tryAgain = mLayout.findViewById(R.id.button_try_again);
+ mNegativeButton = mLayout.findViewById(R.id.button2);
+ mPositiveButton = mLayout.findViewById(R.id.button1);
icon.setContentDescription(getResources().getString(getIconDescriptionResourceId()));
@@ -189,12 +195,15 @@
setDismissesDialog(leftSpace);
setDismissesDialog(rightSpace);
- negative.setOnClickListener((View v) -> {
+ mNegativeButton.setOnClickListener((View v) -> {
mCallback.onNegativePressed();
});
- positive.setOnClickListener((View v) -> {
- mCallback.onPositivePressed();
+ mPositiveButton.setOnClickListener((View v) -> {
+ updateState(STATE_AUTHENTICATED);
+ mHandler.postDelayed(() -> {
+ mCallback.onPositivePressed();
+ }, getDelayAfterAuthenticatedDurationMs());
});
tryAgain.setOnClickListener((View v) -> {
@@ -215,7 +224,6 @@
final TextView title = mLayout.findViewById(R.id.title);
final TextView subtitle = mLayout.findViewById(R.id.subtitle);
final TextView description = mLayout.findViewById(R.id.description);
- final Button negative = mLayout.findViewById(R.id.button2);
final ImageView backgroundView = mLayout.findViewById(R.id.background);
if (mUserManager.isManagedProfile(mUserId)) {
@@ -229,6 +237,9 @@
backgroundView.setBackgroundColor(R.color.biometric_dialog_dim_color);
}
+ mNegativeButton.setVisibility(View.VISIBLE);
+ mErrorText.setVisibility(View.VISIBLE);
+
if (RotationUtils.getRotation(mContext) != RotationUtils.ROTATION_NONE) {
mDialog.getLayoutParams().width = (int) mDialogWidth;
}
@@ -258,7 +269,7 @@
description.setText(descriptionText);
}
- negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
+ mNegativeButton.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
showTryAgainButton(mPendingShowTryAgain);
showConfirmationButton(mPendingShowConfirm);
@@ -280,10 +291,32 @@
mSkipIntro = false;
}
+ protected void updateIcon(int lastState, int newState) {
+ final Drawable icon = getAnimationForTransition(lastState, newState);
+ if (icon == null) {
+ Log.e(TAG, "Animation not found");
+ return;
+ }
+
+ final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
+ ? (AnimatedVectorDrawable) icon
+ : null;
+
+ final ImageView imageView = getLayout().findViewById(R.id.biometric_icon);
+ imageView.setImageDrawable(icon);
+
+ if (animation != null && shouldAnimateForTransition(lastState, newState)) {
+ animation.forceAnimationOnUI();
+ animation.start();
+ }
+ }
+
private void setDismissesDialog(View v) {
v.setClickable(true);
v.setOnTouchListener((View view, MotionEvent event) -> {
- mCallback.onUserCanceled();
+ if (mLastState != STATE_AUTHENTICATED) {
+ mCallback.onUserCanceled();
+ }
return true;
});
}
@@ -302,6 +335,7 @@
showTryAgainButton(false /* show */);
mPendingShowTryAgain = false;
mPendingShowConfirm = false;
+ updateState(STATE_NONE);
}
};
@@ -362,11 +396,11 @@
}
public void showConfirmationButton(boolean show) {
- final Button positive = mLayout.findViewById(R.id.button1);
if (show) {
- positive.setVisibility(View.VISIBLE);
+ updateState(STATE_PENDING_CONFIRMATION);
+ mPositiveButton.setVisibility(View.VISIBLE);
} else {
- positive.setVisibility(View.GONE);
+ mPositiveButton.setVisibility(View.GONE);
}
}
@@ -411,7 +445,15 @@
mCallback.onErrorShown();
}
- private void updateState(int newState) {
+ public void updateState(int newState) {
+ if (newState == STATE_PENDING_CONFIRMATION) {
+ mErrorText.setVisibility(View.INVISIBLE);
+ } else if (newState == STATE_AUTHENTICATED) {
+ mPositiveButton.setVisibility(View.GONE);
+ mNegativeButton.setVisibility(View.GONE);
+ mErrorText.setVisibility(View.INVISIBLE);
+ }
+
updateIcon(mLastState, newState);
mLastState = newState;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
index feef3a6d..de3f947 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
@@ -18,16 +18,18 @@
import android.content.Context;
import android.graphics.drawable.Drawable;
-import android.widget.ImageView;
import com.android.systemui.R;
/**
* This class loads the view for the system-provided dialog. The view consists of:
- * Application Icon, Title, Subtitle, Description, Fingerprint Icon, Error/Help message area,
+ * Application Icon, Title, Subtitle, Description, Biometric Icon, Error/Help message area,
* and positive/negative buttons.
*/
public class FaceDialogView extends BiometricDialogView {
+
+ private static final int HIDE_DIALOG_DELAY = 500; // ms
+
public FaceDialogView(Context context,
DialogViewCallback callback) {
super(context, callback);
@@ -53,10 +55,46 @@
}
@Override
- protected void updateIcon(int lastState, int newState) {
- Drawable icon = mContext.getDrawable(R.drawable.face_dialog_icon);
+ protected boolean shouldAnimateForTransition(int oldState, int newState) {
+ if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
+ return false;
+ } else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
+ return true;
+ } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATING) {
+ return true;
+ } else if (oldState == STATE_AUTHENTICATING && newState == STATE_PENDING_CONFIRMATION) {
+ return true;
+ } else if (oldState == STATE_PENDING_CONFIRMATION && newState == STATE_AUTHENTICATED) {
+ return true;
+ } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
+ return true;
+ }
+ return false;
+ }
- final ImageView faceIcon = getLayout().findViewById(R.id.biometric_icon);
- faceIcon.setImageDrawable(icon);
+ @Override
+ protected int getDelayAfterAuthenticatedDurationMs() {
+ return HIDE_DIALOG_DELAY;
+ }
+
+ @Override
+ protected Drawable getAnimationForTransition(int oldState, int newState) {
+ int iconRes;
+ if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
+ iconRes = R.drawable.face_dialog_face_to_error;
+ } else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
+ iconRes = R.drawable.face_dialog_face_to_error;
+ } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATING) {
+ iconRes = R.drawable.face_dialog_error_to_face;
+ } else if (oldState == STATE_AUTHENTICATING && newState == STATE_PENDING_CONFIRMATION) {
+ iconRes = R.drawable.face_dialog_face_gray_to_face_blue;
+ } else if (oldState == STATE_PENDING_CONFIRMATION && newState == STATE_AUTHENTICATED) {
+ iconRes = R.drawable.face_dialog_face_blue_to_checkmark;
+ } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
+ iconRes = R.drawable.face_dialog_face_gray_to_checkmark;
+ } else {
+ return null;
+ }
+ return mContext.getDrawable(iconRes);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
index 38a69a9..1a6cee2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
@@ -17,21 +17,21 @@
package com.android.systemui.biometrics;
import android.content.Context;
-import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
-import android.util.Log;
-import android.widget.ImageView;
import com.android.systemui.R;
/**
* This class loads the view for the system-provided dialog. The view consists of:
- * Application Icon, Title, Subtitle, Description, Fingerprint Icon, Error/Help message area,
+ * Application Icon, Title, Subtitle, Description, Biometric Icon, Error/Help message area,
* and positive/negative buttons.
*/
public class FingerprintDialogView extends BiometricDialogView {
- private static final String TAG = "FingerprintDialogView";
+ public FingerprintDialogView(Context context,
+ DialogViewCallback callback) {
+ super(context, callback);
+ }
@Override
protected int getHintStringResourceId() {
return R.string.fingerprint_dialog_touch_sensor;
@@ -48,33 +48,7 @@
}
@Override
- protected void updateIcon(int lastState, int newState) {
- Drawable icon = getAnimationForTransition(lastState, newState);
-
- if (icon == null) {
- Log.e(TAG, "Animation not found");
- return;
- }
-
- final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
- ? (AnimatedVectorDrawable) icon
- : null;
-
- final ImageView fingerprintIcon = getLayout().findViewById(R.id.biometric_icon);
- fingerprintIcon.setImageDrawable(icon);
-
- if (animation != null && shouldAnimateForTransition(lastState, newState)) {
- animation.forceAnimationOnUI();
- animation.start();
- }
- }
-
- public FingerprintDialogView(Context context,
- DialogViewCallback callback) {
- super(context, callback);
- }
-
- private boolean shouldAnimateForTransition(int oldState, int newState) {
+ protected boolean shouldAnimateForTransition(int oldState, int newState) {
if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
return false;
} else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
@@ -88,7 +62,13 @@
return false;
}
- private Drawable getAnimationForTransition(int oldState, int newState) {
+ @Override
+ protected int getDelayAfterAuthenticatedDurationMs() {
+ return 0;
+ }
+
+ @Override
+ protected Drawable getAnimationForTransition(int oldState, int newState) {
int iconRes;
if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
iconRes = R.drawable.fingerprint_dialog_fp_to_error;
@@ -98,7 +78,7 @@
iconRes = R.drawable.fingerprint_dialog_error_to_fp;
} else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
// TODO(b/77328470): add animation when fingerprint is authenticated
- iconRes = R.drawable.fingerprint_dialog_error_to_fp;
+ iconRes = R.drawable.fingerprint_dialog_fp_to_error;
} else {
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 04362c1..b6fc355 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -85,6 +85,7 @@
mProxCallback = proxCallback;
mResolver = mContext.getContentResolver();
+ boolean alwaysOn = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT);
mSensors = new TriggerSensor[] {
new TriggerSensor(
mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION),
@@ -116,7 +117,7 @@
new PluginSensor(
new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
Settings.Secure.DOZE_WAKE_SCREEN_GESTURE,
- mConfig.wakeScreenGestureAvailable(),
+ mConfig.wakeScreenGestureAvailable() && alwaysOn,
DozeLog.REASON_SENSOR_WAKE_UP,
false /* reports touch coordinates */,
false /* touchscreen */),
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index dc11b4c..19a7cea 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -132,9 +132,9 @@
}
@Override
- public void disable(int state1, int state2, boolean animate) {
+ public void disable(int displayId, int state1, int state2, boolean animate) {
final boolean disabled = (state2 & DISABLE2_GLOBAL_ACTIONS) != 0;
- if (disabled == mDisabled) return;
+ if (displayId != mContext.getDisplayId() || disabled == mDisabled) return;
mDisabled = disabled;
if (disabled && mGlobalActions != null) {
mGlobalActions.dismissDialog();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 34d30fe..087a826 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -215,7 +215,10 @@
}
@Override
- public void disable(int state1, int state2, boolean animate) {
+ public void disable(int displayId, int state1, int state2, boolean animate) {
+ if (displayId != getContext().getDisplayId()) {
+ return;
+ }
state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index d7d3981..7c937a9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -17,6 +17,7 @@
import android.app.ActivityManager;
import android.app.AlertDialog;
+import android.app.admin.DevicePolicyEventLogger;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -30,6 +31,7 @@
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.util.Log;
+import android.util.StatsLog;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
@@ -119,6 +121,9 @@
private void handleClick() {
showDeviceMonitoringDialog();
+ DevicePolicyEventLogger
+ .createEvent(StatsLog.DEVICE_POLICY_EVENT__EVENT_ID__DO_USER_INFO_CLICKED)
+ .write();
}
public void showDeviceMonitoringDialog() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index bec027f..7224599 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -63,6 +63,8 @@
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.privacy.OngoingPrivacyDialog;
import com.android.systemui.privacy.PrivacyItem;
@@ -75,8 +77,6 @@
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.Clock;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.policy.DateView;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.ZenModeController;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index f13b565..0fc4fe7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -54,8 +54,10 @@
}
@Override
- public void appTransitionFinished() {
- mImpl.onAppTransitionFinished();
+ public void appTransitionFinished(int displayId) {
+ if (mContext.getDisplayId() == displayId) {
+ mImpl.onAppTransitionFinished();
+ }
}
public void growRecents() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 95019ee..6a01563 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -16,18 +16,30 @@
package com.android.systemui.statusbar;
+import static android.app.StatusBarManager.DISABLE2_NONE;
+import static android.app.StatusBarManager.DISABLE_NONE;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.systemui.statusbar.phone.StatusBar.ONLY_CORE_APPS;
import android.app.StatusBarManager;
+import android.app.StatusBarManager.Disable2Flags;
+import android.app.StatusBarManager.DisableFlags;
+import android.app.StatusBarManager.WindowType;
+import android.app.StatusBarManager.WindowVisibleState;
import android.content.ComponentName;
+import android.content.Context;
import android.graphics.Rect;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.display.DisplayManager;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.util.Pair;
+import android.util.SparseArray;
import androidx.annotation.VisibleForTesting;
@@ -47,7 +59,8 @@
* coalescing these calls so they don't stack up. For the calls
* are coalesced, note that they are all idempotent.
*/
-public class CommandQueue extends IStatusBar.Stub implements CallbackController<Callbacks> {
+public class CommandQueue extends IStatusBar.Stub implements CallbackController<Callbacks>,
+ DisplayManager.DisplayListener {
private static final int INDEX_MASK = 0xffff;
private static final int MSG_SHIFT = 16;
private static final int MSG_MASK = 0xffff << MSG_SHIFT;
@@ -112,8 +125,8 @@
private final Object mLock = new Object();
private ArrayList<Callbacks> mCallbacks = new ArrayList<>();
private Handler mHandler = new H(Looper.getMainLooper());
- private int mDisable1;
- private int mDisable2;
+ /** A map of display id - disable flag pair */
+ private SparseArray<Pair<Integer, Integer>> mDisplayDisabled = new SparseArray<>();
/**
* These methods are called back on the main thread.
@@ -121,17 +134,63 @@
public interface Callbacks {
default void setIcon(String slot, StatusBarIcon icon) { }
default void removeIcon(String slot) { }
- default void disable(int state1, int state2, boolean animate) { }
+
+ /**
+ * Called to notify that disable flags are updated.
+ * @see IStatusBar#disable(int, int, int).
+ *
+ * @param displayId The id of the display to notify.
+ * @param state1 The combination of following DISABLE_* flags:
+ * @param state2 The combination of following DISABLE2_* flags:
+ * @param animate {@code true} to show animations.
+ */
+ default void disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2,
+ boolean animate) { }
default void animateExpandNotificationsPanel() { }
default void animateCollapsePanels(int flags, boolean force) { }
default void togglePanel() { }
default void animateExpandSettingsPanel(String obj) { }
- default void setSystemUiVisibility(int vis, int fullscreenStackVis,
+
+ /**
+ * Called to notify visibility flag changes.
+ * @see IStatusBar#setSystemUiVisibility(int, int, int, int, int, Rect, Rect).
+ *
+ * @param displayId The id of the display to notify.
+ * @param vis The visibility flags except SYSTEM_UI_FLAG_LIGHT_STATUS_BAR which will
+ * be reported separately in fullscreenStackVis and dockedStackVis.
+ * @param fullscreenStackVis The flags which only apply in the region of the fullscreen
+ * stack, which is currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
+ * @param dockedStackVis The flags that only apply in the region of the docked stack, which
+ * is currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
+ * @param mask Which flags to change.
+ * @param fullscreenStackBounds The current bounds of the fullscreen stack, in screen
+ * coordinates.
+ * @param dockedStackBounds The current bounds of the docked stack, in screen coordinates.
+ */
+ default void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
}
- default void topAppWindowChanged(boolean visible) { }
- default void setImeWindowStatus(IBinder token, int vis, int backDisposition,
- boolean showImeSwitcher) { }
+
+ /**
+ * 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.
+ * @param token IME token.
+ * @param vis IME visibility.
+ * @param backDisposition Disposition mode of back button. It should be one of below flags:
+ * @param showImeSwitcher {@code true} to show IME switch button.
+ */
+ default void setImeWindowStatus(int displayId, IBinder token, int vis,
+ @BackDispositionMode int backDisposition, boolean showImeSwitcher) { }
default void showRecentApps(boolean triggeredFromAltTab) { }
default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { }
default void toggleRecentApps() { }
@@ -140,12 +199,56 @@
default void dismissKeyboardShortcutsMenu() { }
default void toggleKeyboardShortcutsMenu(int deviceId) { }
default void cancelPreloadRecentApps() { }
- default void setWindowState(int window, int state) { }
+
+ /**
+ * Called to notify window state changes.
+ * @see IStatusBar#setWindowState(int, int, int)
+ *
+ * @param displayId The id of the display to notify.
+ * @param window Window type. It should be one of {@link StatusBarManager#WINDOW_STATUS_BAR}
+ * or {@link StatusBarManager#WINDOW_NAVIGATION_BAR}
+ * @param state Window visible state.
+ */
+ default void setWindowState(int displayId, @WindowType int window,
+ @WindowVisibleState int state) { }
default void showScreenPinningRequest(int taskId) { }
- default void appTransitionPending(boolean forced) { }
- default void appTransitionCancelled() { }
- default void appTransitionStarting(long startTime, long duration, boolean forced) { }
- default void appTransitionFinished() { }
+
+ /**
+ * Called to notify System UI that an application transition is pending.
+ * @see IStatusBar#appTransitionPending(int).
+ *
+ * @param displayId The id of the display to notify.
+ * @param forced {@code true} to force transition pending.
+ */
+ default void appTransitionPending(int displayId, boolean forced) { }
+
+ /**
+ * Called to notify System UI that an application transition is canceled.
+ * @see IStatusBar#appTransitionCancelled(int).
+ *
+ * @param displayId The id of the display to notify.
+ */
+ default void appTransitionCancelled(int displayId) { }
+
+ /**
+ * Called to notify System UI that an application transition is starting.
+ * @see IStatusBar#appTransitionStarting(int, long, long).
+ *
+ * @param displayId The id of the display to notify.
+ * @param startTime Transition start time.
+ * @param duration Transition duration.
+ * @param forced {@code true} to force transition pending.
+ */
+ default void appTransitionStarting(
+ int displayId, long startTime, long duration, boolean forced) { }
+
+ /**
+ * Called to notify System UI that an application transition is finished.
+ * @see IStatusBar#appTransitionFinished(int)
+ *
+ * @param displayId The id of the display to notify.
+ */
+ default void appTransitionFinished(int displayId) { }
default void showAssistDisclosure() { }
default void startAssist(Bundle args) { }
default void onCameraLaunchGestureDetected(int source) { }
@@ -176,18 +279,43 @@
}
@VisibleForTesting
- public CommandQueue() {
+ public CommandQueue(Context context) {
+ context.getSystemService(DisplayManager.class).registerDisplayListener(this, mHandler);
+ // We always have default display.
+ setDisabled(DEFAULT_DISPLAY, DISABLE_NONE, DISABLE2_NONE);
}
+ @Override
+ public void onDisplayAdded(int displayId) { }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ mDisplayDisabled.remove(displayId);
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) { }
+
+ // TODO(b/118592525): add multi-display support if needed.
public boolean panelsEnabled() {
- return (mDisable1 & StatusBarManager.DISABLE_EXPAND) == 0
- && (mDisable2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
+ final int disabled1 = getDisabled1(DEFAULT_DISPLAY);
+ final int disabled2 = getDisabled2(DEFAULT_DISPLAY);
+ return (disabled1 & StatusBarManager.DISABLE_EXPAND) == 0
+ && (disabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
&& !ONLY_CORE_APPS;
}
public void addCallback(Callbacks callbacks) {
mCallbacks.add(callbacks);
- callbacks.disable(mDisable1, mDisable2, false /* animate */);
+ // TODO(b/117478341): find a better way to pass disable flags by display.
+ for (int i = 0; i < mDisplayDisabled.size(); i++) {
+ int displayId = mDisplayDisabled.keyAt(i);
+ int disabled1 = getDisabled1(displayId);
+ int disabled2 = getDisabled2(displayId);
+ callbacks.disable(displayId, disabled1, disabled2, false /* animate */);
+ }
}
public void removeCallback(Callbacks callbacks) {
@@ -209,12 +337,21 @@
}
}
- public void disable(int state1, int state2, boolean animate) {
+ /**
+ * Called to notify that disable flags are updated.
+ * @see Callbacks#disable(int, int, int, boolean).
+ */
+ public void disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2,
+ boolean animate) {
synchronized (mLock) {
- mDisable1 = state1;
- mDisable2 = state2;
+ setDisabled(displayId, state1, state2);
mHandler.removeMessages(MSG_DISABLE);
- Message msg = mHandler.obtainMessage(MSG_DISABLE, state1, state2, animate);
+ final SomeArgs args = SomeArgs.obtain();
+ args.argi1 = displayId;
+ args.argi2 = state1;
+ args.argi3 = state2;
+ args.argi4 = animate ? 1 : 0;
+ Message msg = mHandler.obtainMessage(MSG_DISABLE, args);
if (Looper.myLooper() == mHandler.getLooper()) {
// If its the right looper execute immediately so hides can be handled quickly.
mHandler.handleMessage(msg);
@@ -225,14 +362,42 @@
}
}
- // TODO(b/117478341): Add multi-display support.
@Override
- public void disable(int displayId, int state1, int state2) {
- disable(state1, state2, true);
+ public void disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2) {
+ disable(displayId, state1, state2, true);
}
- public void recomputeDisableFlags(boolean animate) {
- disable(mDisable1, mDisable2, animate);
+ /**
+ * Apply current disable flags by {@link CommandQueue#disable(int, int, int, boolean)}.
+ *
+ * @param displayId The id of the display to notify.
+ * @param animate {@code true} to show animations.
+ */
+ public void recomputeDisableFlags(int displayId, boolean animate) {
+ int disabled1 = getDisabled1(displayId);
+ int disabled2 = getDisabled2(displayId);
+ disable(displayId, disabled1, disabled2, animate);
+ }
+
+ private void setDisabled(int displayId, int disabled1, int disabled2) {
+ mDisplayDisabled.put(displayId, new Pair<>(disabled1, disabled2));
+ }
+
+ private int getDisabled1(int displayId) {
+ return getDisabled(displayId).first;
+ }
+
+ private int getDisabled2(int displayId) {
+ return getDisabled(displayId).second;
+ }
+
+ private Pair<Integer, Integer> getDisabled(int displayId) {
+ Pair<Integer, Integer> disablePair = mDisplayDisabled.get(displayId);
+ if (disablePair == null) {
+ disablePair = new Pair<>(DISABLE_NONE, DISABLE2_NONE);
+ mDisplayDisabled.put(displayId, disablePair);
+ }
+ return disablePair;
}
public void animateExpandNotificationsPanel() {
@@ -270,7 +435,6 @@
}
}
- // TODO(b/117478341): Add multi-display support.
@Override
public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
@@ -278,34 +442,38 @@
// Don't coalesce these, since it might have one time flags set such as
// STATUS_BAR_UNHIDE which might get lost.
SomeArgs args = SomeArgs.obtain();
- args.argi1 = vis;
- args.argi2 = fullscreenStackVis;
- args.argi3 = dockedStackVis;
- args.argi4 = mask;
+ args.argi1 = displayId;
+ args.argi2 = vis;
+ args.argi3 = fullscreenStackVis;
+ args.argi4 = dockedStackVis;
+ args.argi5 = mask;
args.arg1 = fullscreenStackBounds;
args.arg2 = dockedStackBounds;
mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget();
}
}
- // TODO(b/117478341): Add multi-display support.
@Override
public void topAppWindowChanged(int displayId, boolean menuVisible) {
synchronized (mLock) {
mHandler.removeMessages(MSG_TOP_APP_WINDOW_CHANGED);
- mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, menuVisible ? 1 : 0, 0,
- null).sendToTarget();
+ mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED,
+ displayId, menuVisible ? 1 : 0, null).sendToTarget();
}
}
- // TODO(b/117478341): Add multi-display support.
@Override
public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
synchronized (mLock) {
mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
- Message m = mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, vis, backDisposition, token);
- m.getData().putBoolean(SHOW_IME_SWITCHER_KEY, showImeSwitcher);
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = displayId;
+ args.argi2 = vis;
+ args.argi3 = backDisposition;
+ args.argi4 = showImeSwitcher ? 1 : 0;
+ args.arg1 = token;
+ Message m = mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, args);
m.sendToTarget();
}
}
@@ -381,12 +549,11 @@
}
}
- // TODO(b/117478341): Add multi-display support.
@Override
public void setWindowState(int displayId, int window, int state) {
synchronized (mLock) {
// don't coalesce these
- mHandler.obtainMessage(MSG_SET_WINDOW_STATE, window, state, null).sendToTarget();
+ mHandler.obtainMessage(MSG_SET_WINDOW_STATE, displayId, window, state).sendToTarget();
}
}
@@ -397,44 +564,54 @@
}
}
- // TODO(b/117478341): Add multi-display support.
@Override
public void appTransitionPending(int displayId) {
- appTransitionPending(false /* forced */);
+ appTransitionPending(displayId, false /* forced */);
}
- public void appTransitionPending(boolean forced) {
+ /**
+ * Called to notify System UI that an application transition is pending.
+ * @see Callbacks#appTransitionPending(int, boolean)
+ */
+ public void appTransitionPending(int displayId, boolean forced) {
synchronized (mLock) {
- mHandler.obtainMessage(MSG_APP_TRANSITION_PENDING, forced ? 1 : 0, 0).sendToTarget();
+ mHandler.obtainMessage(MSG_APP_TRANSITION_PENDING, displayId, forced ? 1 : 0)
+ .sendToTarget();
}
}
- // TODO(b/117478341): Add multi-display support.
@Override
public void appTransitionCancelled(int displayId) {
synchronized (mLock) {
- mHandler.sendEmptyMessage(MSG_APP_TRANSITION_CANCELLED);
+ mHandler.obtainMessage(MSG_APP_TRANSITION_CANCELLED, displayId).sendToTarget();
}
}
- // TODO(b/117478341): Add multi-display support.
@Override
public void appTransitionStarting(int displayId, long startTime, long duration) {
- appTransitionStarting(startTime, duration, false /* forced */);
+ appTransitionStarting(displayId, startTime, duration, false /* forced */);
}
- public void appTransitionStarting(long startTime, long duration, boolean forced) {
+ /**
+ * Called to notify System UI that an application transition is starting.
+ * @see Callbacks#appTransitionStarting(int, long, long, boolean).
+ */
+ public void appTransitionStarting(int displayId, long startTime, long duration,
+ boolean forced) {
synchronized (mLock) {
- mHandler.obtainMessage(MSG_APP_TRANSITION_STARTING, forced ? 1 : 0, 0,
- Pair.create(startTime, duration)).sendToTarget();
+ final SomeArgs args = SomeArgs.obtain();
+ args.argi1 = displayId;
+ args.argi2 = forced ? 1 : 0;
+ args.arg1 = startTime;
+ args.arg2 = duration;
+ mHandler.obtainMessage(MSG_APP_TRANSITION_STARTING, args).sendToTarget();
}
}
- // TODO(b/117478341): Add multi-display support.
@Override
public void appTransitionFinished(int displayId) {
synchronized (mLock) {
- mHandler.sendEmptyMessage(MSG_APP_TRANSITION_FINISHED);
+ mHandler.obtainMessage(MSG_APP_TRANSITION_FINISHED, displayId).sendToTarget();
}
}
@@ -619,8 +796,10 @@
break;
}
case MSG_DISABLE:
+ SomeArgs args = (SomeArgs) msg.obj;
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).disable(msg.arg1, msg.arg2, (Boolean) msg.obj);
+ mCallbacks.get(i).disable(args.argi1, args.argi2, args.argi3,
+ args.argi4 != 0 /* animate */);
}
break;
case MSG_EXPAND_NOTIFICATIONS:
@@ -644,22 +823,23 @@
}
break;
case MSG_SET_SYSTEMUI_VISIBILITY:
- SomeArgs args = (SomeArgs) msg.obj;
+ args = (SomeArgs) msg.obj;
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).setSystemUiVisibility(args.argi1, args.argi2, args.argi3,
- args.argi4, (Rect) args.arg1, (Rect) args.arg2);
+ args.argi4, args.argi5, (Rect) args.arg1, (Rect) args.arg2);
}
args.recycle();
break;
case MSG_TOP_APP_WINDOW_CHANGED:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).topAppWindowChanged(msg.arg1 != 0);
+ mCallbacks.get(i).topAppWindowChanged(msg.arg1, msg.arg2 != 0);
}
break;
case MSG_SHOW_IME_BUTTON:
+ args = (SomeArgs) msg.obj;
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).setImeWindowStatus((IBinder) msg.obj, msg.arg1, msg.arg2,
- msg.getData().getBoolean(SHOW_IME_SWITCHER_KEY, false));
+ mCallbacks.get(i).setImeWindowStatus(args.argi1, (IBinder) args.arg1,
+ args.argi2, args.argi3, args.argi4 != 0 /* showImeSwitcher */);
}
break;
case MSG_SHOW_RECENT_APPS:
@@ -699,7 +879,7 @@
break;
case MSG_SET_WINDOW_STATE:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).setWindowState(msg.arg1, msg.arg2);
+ mCallbacks.get(i).setWindowState(msg.arg1, msg.arg2, (int) msg.obj);
}
break;
case MSG_SHOW_SCREEN_PIN_REQUEST:
@@ -709,24 +889,24 @@
break;
case MSG_APP_TRANSITION_PENDING:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).appTransitionPending(msg.arg1 != 0);
+ mCallbacks.get(i).appTransitionPending(msg.arg1, msg.arg2 != 0);
}
break;
case MSG_APP_TRANSITION_CANCELLED:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).appTransitionCancelled();
+ mCallbacks.get(i).appTransitionCancelled(msg.arg1);
}
break;
case MSG_APP_TRANSITION_STARTING:
+ args = (SomeArgs) msg.obj;
for (int i = 0; i < mCallbacks.size(); i++) {
- Pair<Long, Long> data = (Pair<Long, Long>) msg.obj;
- mCallbacks.get(i).appTransitionStarting(data.first, data.second,
- msg.arg1 != 0);
+ mCallbacks.get(i).appTransitionStarting(args.argi1, (long) args.arg1,
+ (long) args.arg2, args.argi2 != 0 /* forced */);
}
break;
case MSG_APP_TRANSITION_FINISHED:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).appTransitionFinished();
+ mCallbacks.get(i).appTransitionFinished(msg.arg1);
}
break;
case MSG_ASSIST_DISCLOSURE:
@@ -858,7 +1038,7 @@
public static class CommandQueueStart extends SystemUI {
@Override
public void start() {
- putComponent(CommandQueue.class, new CommandQueue());
+ putComponent(CommandQueue.class, new CommandQueue(mContext));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
deleted file mode 100644
index 78172f1..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
-
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.Display;
-import android.view.IWindowManager;
-import android.view.View;
-import android.view.WindowManagerGlobal;
-
-import com.android.systemui.statusbar.phone.NavigationBarFragment;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-
-/**
- * A controller to handle external navigation bars
- */
-@Singleton
-public class DisplayNavigationBarController implements DisplayListener {
-
- private static final String TAG = DisplayNavigationBarController.class.getName();
-
- private final Context mContext;
- private final Handler mHandler;
- private final DisplayManager mDisplayManager;
-
- /** A displayId - nav bar mapping */
- private SparseArray<NavigationBarFragment> mExternalNavigationBarMap = new SparseArray<>();
-
- @Inject
- public DisplayNavigationBarController(Context context,
- @Named(MAIN_HANDLER_NAME) Handler handler) {
- mContext = context;
- mHandler = handler;
- mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
-
- registerListener();
- }
-
- @Override
- public void onDisplayAdded(int displayId) {
- final Display display = mDisplayManager.getDisplay(displayId);
- addExternalNavigationBar(display);
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- final NavigationBarFragment navBar = mExternalNavigationBarMap.get(displayId);
- if (navBar != null) {
- final View navigationView = navBar.getView().getRootView();
- WindowManagerGlobal.getInstance().removeView(navigationView, true);
- mExternalNavigationBarMap.remove(displayId);
- }
- }
-
- @Override
- public void onDisplayChanged(int displayId) {
- }
-
- /** Create external navigation bars when car/status bar initializes */
- public void createNavigationBars() {
- // Add external navigation bars if more than one displays exist.
- final Display[] displays = mDisplayManager.getDisplays();
- for (Display display : displays) {
- addExternalNavigationBar(display);
- }
- }
-
- /** remove external navigation bars and unset everything related to external navigation bars */
- public void destroy() {
- unregisterListener();
- if (mExternalNavigationBarMap.size() > 0) {
- for (int i = 0; i < mExternalNavigationBarMap.size(); i++) {
- final View navigationWindow = mExternalNavigationBarMap.valueAt(i)
- .getView().getRootView();
- WindowManagerGlobal.getInstance()
- .removeView(navigationWindow, true /* immediate */);
- }
- mExternalNavigationBarMap.clear();
- }
- }
-
- private void registerListener() {
- mDisplayManager.registerDisplayListener(this, mHandler);
- }
-
- private void unregisterListener() {
- mDisplayManager.unregisterDisplayListener(this);
- }
-
- /**
- * Add a phone navigation bar on an external display if the display supports system decorations.
- *
- * @param display the display to add navigation bar on
- */
- private void addExternalNavigationBar(Display display) {
- if (display == null || display.getDisplayId() == DEFAULT_DISPLAY
- || !display.supportsSystemDecorations()) {
- return;
- }
-
- final int displayId = display.getDisplayId();
- final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
-
- try {
- if (!wms.hasNavigationBar(displayId)) {
- return;
- }
- } catch (RemoteException e) {
- // Cannot get wms, just return with warning message.
- Log.w(TAG, "Cannot get WindowManager.");
- return;
- }
- final Context externalDisplayContext = mContext.createDisplayContext(display);
- NavigationBarFragment.create(externalDisplayContext, (tag, fragment) -> {
- final NavigationBarFragment navBar = (NavigationBarFragment) fragment;
- // TODO(b/115978725): handle external nav bars sysuiVisibility
- navBar.setCurrentSysuiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
- mExternalNavigationBarMap.append(displayId, navBar);
- });
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index b39a96d..e217777 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -31,8 +31,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.AlphaOptimizedLinearLayout;
import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
new file mode 100644
index 0000000..9740d1d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.View;
+import android.view.WindowManagerGlobal;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.NavigationBarFragment;
+import com.android.systemui.statusbar.phone.NavigationBarView;
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+
+/** A controller to handle navigation bars. */
+@Singleton
+public class NavigationBarController implements DisplayListener {
+
+ private static final String TAG = NavigationBarController.class.getName();
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final DisplayManager mDisplayManager;
+
+ /** A displayId - nav bar maps. */
+ private SparseArray<NavigationBarFragment> mNavigationBars = new SparseArray<>();
+
+ @Inject
+ public NavigationBarController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) {
+ mContext = context;
+ mHandler = handler;
+ mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ mDisplayManager.registerDisplayListener(this, mHandler);
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ Display display = mDisplayManager.getDisplay(displayId);
+ createNavigationBar(display);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ removeNavigationBar(displayId);
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ }
+
+ // TODO(b/117478341): I use {@code includeDefaultDisplay} to make this method compatible to
+ // CarStatusBar because they have their own nav bar. Think about a better way for it.
+ /**
+ * Creates navigation bars when car/status bar initializes.
+ *
+ * @param includeDefaultDisplay {@code true} to create navigation bar on default display.
+ */
+ public void createNavigationBars(final boolean includeDefaultDisplay) {
+ Display[] displays = mDisplayManager.getDisplays();
+ for (Display display : displays) {
+ if (includeDefaultDisplay || display.getDisplayId() != DEFAULT_DISPLAY) {
+ createNavigationBar(display);
+ }
+ }
+ }
+
+ /**
+ * Adds a navigation bar on default display or an external display if the display supports
+ * system decorations.
+ *
+ * @param display the display to add navigation bar on.
+ */
+ private void createNavigationBar(Display display) {
+ if (display == null
+ || (display.getDisplayId() != DEFAULT_DISPLAY
+ && !display.supportsSystemDecorations())) {
+ return;
+ }
+
+ final int displayId = display.getDisplayId();
+ final boolean isOnDefaultDisplay = displayId == DEFAULT_DISPLAY;
+ final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
+
+ try {
+ if (!wms.hasNavigationBar(displayId)) {
+ return;
+ }
+ } catch (RemoteException e) {
+ // Cannot get wms, just return with warning message.
+ Log.w(TAG, "Cannot get WindowManager.");
+ return;
+ }
+ final Context context = isOnDefaultDisplay
+ ? mContext
+ : mContext.createDisplayContext(display);
+ NavigationBarFragment.create(context, (tag, fragment) -> {
+ NavigationBarFragment navBar = (NavigationBarFragment) fragment;
+
+ // Unfortunately, we still need it because status bar needs LightBarController
+ // before notifications creation. We cannot directly use getLightBarController()
+ // from NavigationBarFragment directly.
+ LightBarController lightBarController = isOnDefaultDisplay
+ ? Dependency.get(LightBarController.class)
+ : new LightBarController(context,
+ Dependency.get(DarkIconDispatcher.class),
+ Dependency.get(BatteryController.class));
+ navBar.setLightBarController(lightBarController);
+
+ // TODO(b/118592525): to support multi-display, we start to add something which is
+ // per-display, while others may be global. I think it's time to add
+ // a new class maybe named DisplayDependency to solve per-display
+ // Dependency problem.
+ AutoHideController autoHideController = isOnDefaultDisplay
+ ? Dependency.get(AutoHideController.class)
+ : new AutoHideController(context, mHandler);
+ navBar.setAutoHideController(autoHideController);
+ navBar.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
+ mNavigationBars.append(displayId, navBar);
+ });
+ }
+
+ /** Removes navigation bars. */
+ public void destroy() {
+ mDisplayManager.unregisterDisplayListener(this);
+ if (mNavigationBars.size() > 0) {
+ for (int i = 0; i < mNavigationBars.size(); i++) {
+ int displayId = mNavigationBars.keyAt(i);
+ removeNavigationBar(displayId);
+ }
+ mNavigationBars.clear();
+ }
+ }
+
+ private void removeNavigationBar(int displayId) {
+ NavigationBarFragment navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ View navigationWindow = navBar.getView().getRootView();
+ WindowManagerGlobal.getInstance()
+ .removeView(navigationWindow, true /* immediate */);
+ mNavigationBars.remove(displayId);
+ }
+ }
+
+ /** @see NavigationBarFragment#checkNavBarModes() */
+ public void checkNavBarModes(int displayId) {
+ NavigationBarFragment navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.checkNavBarModes();
+ }
+ }
+
+ /** @see NavigationBarFragment#finishBarAnimations() */
+ public void finishBarAnimations(int displayId) {
+ NavigationBarFragment navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.finishBarAnimations();
+ }
+ }
+
+ /** @see NavigationBarFragment#touchAutoDim() */
+ public void touchAutoDim(int displayId) {
+ NavigationBarFragment navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.touchAutoDim();
+ }
+ }
+
+ /** @see NavigationBarFragment#transitionTo(int, boolean) */
+ public void transitionTo(int displayId, @TransitionMode int barMode, boolean animate) {
+ NavigationBarFragment navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.transitionTo(barMode, animate);
+ }
+ }
+
+ /** @see NavigationBarFragment#disableAnimationsDuringHide(long) */
+ public void disableAnimationsDuringHide(int displayId, long delay) {
+ NavigationBarFragment navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.disableAnimationsDuringHide(delay);
+ }
+ }
+
+ /** @return {@link NavigationBarView} on the default display. */
+ public NavigationBarView getDefaultNavigationBarView() {
+ NavigationBarFragment navBar = mNavigationBars.get(DEFAULT_DISPLAY);
+ return (navBar == null) ? null : (NavigationBarView) navBar.getView();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
index f1a891b..d1b3c3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
@@ -30,8 +30,8 @@
import com.android.settingslib.WirelessUtils;
import com.android.systemui.DemoMode;
import com.android.systemui.Dependency;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index e7b768f..6d2c001 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -41,9 +41,9 @@
import com.android.settingslib.graph.SignalDrawable;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.policy.IconLogger;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 8b61a5b..3c13354 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar;
-import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint;
+import static com.android.systemui.plugins.DarkIconDispatcher.getTint;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index bc89889..4db981d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -16,11 +16,11 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.plugins.DarkIconDispatcher.getTint;
+import static com.android.systemui.plugins.DarkIconDispatcher.isInArea;
import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT;
import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN;
import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON;
-import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint;
-import static com.android.systemui.statusbar.policy.DarkIconDispatcher.isInArea;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -37,8 +37,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.graph.SignalDrawable;
import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
public class StatusBarMobileView extends FrameLayout implements DarkReceiver,
StatusIconDisplayable {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
index 045221f..c5751c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
@@ -16,11 +16,11 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.plugins.DarkIconDispatcher.getTint;
+import static com.android.systemui.plugins.DarkIconDispatcher.isInArea;
import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT;
import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN;
import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON;
-import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint;
-import static com.android.systemui.statusbar.policy.DarkIconDispatcher.isInArea;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -37,8 +37,8 @@
import com.android.settingslib.Utils;
import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
/**
* Start small: StatusBarWifiView will be able to layout from a WifiIconState
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
index beb90b8..d541fae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
public interface StatusIconDisplayable extends DarkReceiver {
String getSlot();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index 27c2837..a51896e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -90,7 +90,7 @@
private static final long INITIALIZATION_DELAY = 400;
private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
private static final int COLOR_INVALID = 1;
- public String key;
+ public final String key;
public StatusBarNotification notification;
public NotificationChannel channel;
public long lastAudiblyAlertedMs;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 32acb8d..610d300 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -174,7 +174,7 @@
NotificationVisibility visibility,
boolean lifetimeExtended,
boolean removedByUser) {
- if (removedByUser && visibility != null && entry.notification != null) {
+ if (removedByUser && visibility != null && entry != null) {
logNotificationClear(key, entry.notification, visibility);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
index 8c8bad2..a5411ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
@@ -87,8 +87,8 @@
try {
drawable = mResolver.resolveImage(target);
- } catch (IOException ex) {
- Log.d(TAG, "PreloadImageTask: Resolve failed from " + target);
+ } catch (IOException | SecurityException ex) {
+ Log.d(TAG, "PreloadImageTask: Resolve failed from " + target, ex);
}
return drawable;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index 588246f..a3e1305 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -81,8 +81,8 @@
Drawable result = null;
try {
result = hasCache() ? mImageCache.get(uri) : resolveImage(uri);
- } catch (IOException ex) {
- Log.d(TAG, "loadImage: Can't load image from " + uri);
+ } catch (IOException | SecurityException ex) {
+ Log.d(TAG, "loadImage: Can't load image from " + uri, ex);
}
return result;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
new file mode 100644
index 0000000..b9425d4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IWindowManager;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/** A controller to control all auto-hide things. */
+public class AutoHideController implements CommandQueue.Callbacks {
+ private static final String TAG = "AutoHideController";
+
+ private final IWindowManager mWindowManagerService;
+
+ private final Handler mHandler;
+ private final NotificationRemoteInputManager mRemoteInputManager;
+ private final CommandQueue mCommandQueue;
+ private StatusBar mStatusBar;
+ private NavigationBarFragment mNavigationBar;
+
+ private int mDisplayId;
+ private int mSystemUiVisibility;
+ // last value sent to window manager
+ private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
+
+ private boolean mAutoHideSuspended;
+
+ private static final long AUTOHIDE_TIMEOUT_MS = 2250;
+
+ private final Runnable mAutoHide = () -> {
+ int requested = mSystemUiVisibility & ~getTransientMask();
+ if (mSystemUiVisibility != requested) {
+ notifySystemUiVisibilityChanged(requested);
+ }
+ };
+
+ @Inject
+ public AutoHideController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) {
+ mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
+ mCommandQueue.addCallback(this);
+ mHandler = handler;
+ mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
+ mWindowManagerService = Dependency.get(IWindowManager.class);
+
+ mDisplayId = context.getDisplayId();
+ }
+
+ void setStatusBar(StatusBar statusBar) {
+ mStatusBar = statusBar;
+ }
+
+ void setNavigationBar(NavigationBarFragment navigationBar) {
+ mNavigationBar = navigationBar;
+ }
+
+ @Override
+ public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+ int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+ if (displayId != mDisplayId) {
+ return;
+ }
+ int oldVal = mSystemUiVisibility;
+ int newVal = (oldVal & ~mask) | (vis & mask);
+ int diff = newVal ^ oldVal;
+
+ if (diff != 0) {
+ mSystemUiVisibility = newVal;
+
+ // ready to unhide
+ if (hasStatusBar() && (vis & View.STATUS_BAR_UNHIDE) != 0) {
+ mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
+ }
+
+ if (hasNavigationBar() && (vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
+ mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
+ }
+
+ // Re-send setSystemUiVisibility to update un-hide status.
+ if (mSystemUiVisibility != newVal) {
+ mCommandQueue.setSystemUiVisibility(mDisplayId, mSystemUiVisibility,
+ fullscreenStackVis, dockedStackVis, mask, fullscreenStackBounds,
+ dockedStackBounds);
+ }
+
+ notifySystemUiVisibilityChanged(mSystemUiVisibility);
+ }
+ }
+
+ private void notifySystemUiVisibilityChanged(int vis) {
+ try {
+ if (mLastDispatchedSystemUiVisibility != vis) {
+ mWindowManagerService.statusBarVisibilityChanged(mDisplayId, vis);
+ mLastDispatchedSystemUiVisibility = vis;
+ }
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Cannot get WindowManager");
+ }
+ }
+
+ void resumeSuspendedAutoHide() {
+ if (mAutoHideSuspended) {
+ scheduleAutoHide();
+ Runnable checkBarModesRunnable = getCheckBarModesRunnable();
+ if (checkBarModesRunnable != null) {
+ mHandler.postDelayed(checkBarModesRunnable, 500); // longer than home -> launcher
+ }
+ }
+ }
+
+ void suspendAutoHide() {
+ mHandler.removeCallbacks(mAutoHide);
+ Runnable checkBarModesRunnable = getCheckBarModesRunnable();
+ if (checkBarModesRunnable != null) {
+ mHandler.removeCallbacks(checkBarModesRunnable);
+ }
+ mAutoHideSuspended = (mSystemUiVisibility & getTransientMask()) != 0;
+ }
+
+ void touchAutoHide() {
+ // update transient bar auto hide
+ if ((hasStatusBar() && mStatusBar.getStatusBarMode() == MODE_SEMI_TRANSPARENT)
+ || hasNavigationBar() && mNavigationBar.isSemiTransparent()) {
+ scheduleAutoHide();
+ } else {
+ cancelAutoHide();
+ }
+ }
+
+ private Runnable getCheckBarModesRunnable() {
+ if (hasStatusBar()) {
+ return () -> mStatusBar.checkBarModes();
+ } else if (hasNavigationBar()) {
+ return () -> mNavigationBar.checkNavBarModes();
+ } else {
+ return null;
+ }
+ }
+
+ private void cancelAutoHide() {
+ mAutoHideSuspended = false;
+ mHandler.removeCallbacks(mAutoHide);
+ }
+
+ private void scheduleAutoHide() {
+ cancelAutoHide();
+ mHandler.postDelayed(mAutoHide, AUTOHIDE_TIMEOUT_MS);
+ }
+
+ void checkUserAutoHide(MotionEvent event) {
+ boolean shouldAutoHide =
+ (mSystemUiVisibility & getTransientMask()) != 0 // a transient bar is revealed.
+ && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar.
+ && event.getX() == 0 && event.getY() == 0;
+ if (hasStatusBar()) {
+ // a touch outside both bars
+ shouldAutoHide &= !mRemoteInputManager.getController().isRemoteInputActive();
+ }
+ if (shouldAutoHide) {
+ userAutoHide();
+ }
+ }
+
+ private void userAutoHide() {
+ cancelAutoHide();
+ mHandler.postDelayed(mAutoHide, 350); // longer than app gesture -> flag clear
+ }
+
+ private int getTransientMask() {
+ int mask = 0;
+ if (hasStatusBar()) {
+ mask |= View.STATUS_BAR_TRANSIENT;
+ }
+ if (hasNavigationBar()) {
+ mask |= View.NAVIGATION_BAR_TRANSIENT;
+ }
+ return mask;
+ }
+
+ private boolean hasNavigationBar() {
+ return mNavigationBar != null;
+ }
+
+ private boolean hasStatusBar() {
+ return mStatusBar != null;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index 3d81473..7905617 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.annotation.IntDef;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
@@ -36,6 +37,9 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
public class BarTransitions {
private static final boolean DEBUG = false;
private static final boolean DEBUG_COLORS = false;
@@ -48,6 +52,18 @@
public static final int MODE_WARNING = 5;
public static final int MODE_LIGHTS_OUT_TRANSPARENT = 6;
+ @IntDef(flag = true, prefix = { "MODE_" }, value = {
+ MODE_OPAQUE,
+ MODE_SEMI_TRANSPARENT,
+ MODE_TRANSLUCENT,
+ MODE_LIGHTS_OUT,
+ MODE_TRANSPARENT,
+ MODE_WARNING,
+ MODE_LIGHTS_OUT_TRANSPARENT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TransitionMode {}
+
public static final int LIGHTS_IN_DURATION = 250;
public static final int LIGHTS_OUT_DURATION = 1500;
public static final int BACKGROUND_DURATION = 200;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 24570ae..f907b65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -70,7 +70,7 @@
private SignalCallback mSignalCallback = new SignalCallback() {
@Override
public void setIsAirplaneMode(NetworkController.IconState icon) {
- mCommandQueue.recomputeDisableFlags(true /* animate */);
+ mCommandQueue.recomputeDisableFlags(getContext().getDisplayId(), true /* animate */);
}
};
@@ -155,7 +155,10 @@
}
@Override
- public void disable(int state1, int state2, boolean animate) {
+ public void disable(int displayId, int state1, int state2, boolean animate) {
+ if (displayId != getContext().getDisplayId()) {
+ return;
+ }
state1 = adjustDisableFlags(state1);
final int old1 = mDisabled1;
final int diff1 = state1 ^ old1;
@@ -362,6 +365,6 @@
@Override
public void onDozingChanged(boolean isDozing) {
- disable(mDisabled1, mDisabled1, false /* animate */);
+ disable(getContext().getDisplayId(), mDisabled1, mDisabled1, false /* animate */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
index 5b44a77..08a10dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
@@ -14,7 +14,8 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint;
+import static com.android.systemui.plugins.DarkIconDispatcher.DEFAULT_ICON_TINT;
+import static com.android.systemui.plugins.DarkIconDispatcher.getTint;
import android.animation.ArgbEvaluator;
import android.content.Context;
@@ -24,7 +25,7 @@
import android.widget.ImageView;
import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -35,7 +36,7 @@
/**
*/
@Singleton
-public class DarkIconDispatcherImpl implements DarkIconDispatcher {
+public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher {
private final LightBarTransitionsController mTransitionsController;
private final Rect mTintArea = new Rect();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index 3425dd2..236c72c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -29,14 +29,14 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.DemoMode;
import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.StatusBarWifiView;
import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 3c8cad7..d1e488a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -25,12 +25,12 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.function.BiConsumer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 5ba59b5..03375d20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -45,13 +45,13 @@
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
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 6632d58..b590ca7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -27,8 +27,8 @@
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -44,7 +44,7 @@
private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f;
- private final DarkIconDispatcher mStatusBarIconController;
+ private final SysuiDarkIconDispatcher mStatusBarIconController;
private final BatteryController mBatteryController;
private BiometricUnlockController mBiometricUnlockController;
@@ -79,16 +79,13 @@
private final Rect mLastDockedBounds = new Rect();
private boolean mQsCustomizing;
- private final Context mContext;
-
@Inject
public LightBarController(Context ctx, DarkIconDispatcher darkIconDispatcher,
BatteryController batteryController) {
mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone));
- mStatusBarIconController = darkIconDispatcher;
+ mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher;
mBatteryController = batteryController;
mBatteryController.addCallback(this);
- mContext = ctx;
}
public void setNavigationBar(LightBarTransitionsController navigationBar) {
@@ -225,9 +222,8 @@
private void updateNavigation() {
if (mNavigationBarController != null) {
- if (!NavBarTintController.isEnabled(mContext)) {
- mNavigationBarController.setIconsDark(mNavigationLight, animateChange());
- }
+ mNavigationBarController.setIconsDark(
+ mNavigationLight, animateChange());
}
}
@@ -268,10 +264,6 @@
pw.println();
- if (mStatusBarIconController != null) {
- mStatusBarIconController.dump(fd, pw, args);
- }
-
LightBarTransitionsController transitionsController =
mStatusBarIconController.getTransitionsController();
if (transitionsController != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index dd07ec4..1944c3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -25,8 +25,8 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
-import android.util.MathUtils;
import android.provider.Settings;
+import android.util.MathUtils;
import android.util.TimeUtils;
import com.android.systemui.Dependency;
@@ -66,6 +66,7 @@
private float mDarkIntensity;
private float mNextDarkIntensity;
private float mDozeAmount;
+ private int mDisplayId;
private final Runnable mTransitionDeferringDoneRunnable = new Runnable() {
@Override
public void run() {
@@ -85,6 +86,7 @@
mStatusBarStateController.addCallback(this);
mDozeAmount = mStatusBarStateController.getDozeAmount();
mContext = context;
+ mDisplayId = mContext.getDisplayId();
}
public void destroy(Context context) {
@@ -104,15 +106,18 @@
}
@Override
- public void appTransitionPending(boolean forced) {
- if (mKeyguardMonitor.isKeyguardGoingAway() && !forced) {
+ public void appTransitionPending(int displayId, boolean forced) {
+ if (mDisplayId != displayId || mKeyguardMonitor.isKeyguardGoingAway() && !forced) {
return;
}
mTransitionPending = true;
}
@Override
- public void appTransitionCancelled() {
+ public void appTransitionCancelled(int displayId) {
+ if (mDisplayId != displayId) {
+ return;
+ }
if (mTransitionPending && mTintChangePending) {
mTintChangePending = false;
animateIconTint(mPendingDarkIntensity, 0 /* delay */, getTintAnimationDuration());
@@ -121,8 +126,9 @@
}
@Override
- public void appTransitionStarting(long startTime, long duration, boolean forced) {
- if (mKeyguardMonitor.isKeyguardGoingAway() && !forced) {
+ public void appTransitionStarting(int displayId, long startTime, long duration,
+ boolean forced) {
+ if (mDisplayId != displayId || mKeyguardMonitor.isKeyguardGoingAway() && !forced) {
return;
}
if (mTransitionPending && mTintChangePending) {
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 2daff2c..6d97d67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -16,14 +16,21 @@
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static android.app.StatusBarManager.WindowType;
+import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
+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;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
@@ -72,6 +79,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.assist.AssistManager;
@@ -83,6 +91,8 @@
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.ContextualButton.ContextButtonListener;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
@@ -111,6 +121,7 @@
/** Allow some time inbetween the long press for back and recents. */
private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
+ private static final long AUTODIM_TIMEOUT_MS = 2250;
private final AccessibilityManagerWrapper mAccessibilityManagerWrapper;
protected final AssistManager mAssistManager;
@@ -119,10 +130,10 @@
protected NavigationBarView mNavigationBarView = null;
- private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
+ private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
private int mNavigationIconHints = 0;
- private int mNavigationBarMode;
+ private @TransitionMode int mNavigationBarMode;
private AccessibilityManager mAccessibilityManager;
private MagnificationContentObserver mMagnificationObserver;
private ContentResolver mContentResolver;
@@ -141,12 +152,16 @@
private int mSystemUiVisibility;
private LightBarController mLightBarController;
+ private AutoHideController mAutoHideController;
private OverviewProxyService mOverviewProxyService;
- private boolean mIsOnDefaultDisplay = true;
+ private int mDisplayId;
+ private boolean mIsOnDefaultDisplay;
public boolean mHomeBlockedThisTouch;
+ private Handler mHandler = Dependency.get(Dependency.MAIN_HANDLER);
+
private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
@Override
public void onConnectionChanged(boolean isConnected) {
@@ -183,17 +198,16 @@
}
};
- private final ContextButtonListener mRotationButtonListener = new ContextButtonListener() {
- @Override
- public void onVisibilityChanged(ContextualButton button, boolean visible) {
- if (visible) {
- // If the button will actually become visible and the navbar is about to hide,
- // tell the statusbar to keep it around for longer
- mStatusBar.touchAutoHide();
- }
+ private final ContextButtonListener mRotationButtonListener = (button, visible) -> {
+ if (visible) {
+ // If the button will actually become visible and the navbar is about to hide,
+ // tell the statusbar to keep it around for longer
+ mAutoHideController.touchAutoHide();
}
};
+ private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true);
+
@Inject
public NavigationBarFragment(AccessibilityManagerWrapper accessibilityManagerWrapper,
DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger,
@@ -251,7 +265,8 @@
final Display display = view.getDisplay();
// It may not have display when running unit test.
if (display != null) {
- mIsOnDefaultDisplay = display.getDisplayId() == Display.DEFAULT_DISPLAY;
+ mDisplayId = display.getDisplayId();
+ mIsOnDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
}
mNavigationBarView.setComponents(mStatusBar.getPanel());
@@ -351,8 +366,11 @@
// ----- CommandQueue Callbacks -----
@Override
- public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
+ public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
+ if (displayId != mDisplayId) {
+ return;
+ }
boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
int hints = mNavigationIconHints;
switch (backDisposition) {
@@ -381,19 +399,21 @@
if (mNavigationBarView != null) {
mNavigationBarView.setNavigationIconHints(hints);
}
- mStatusBar.checkBarModes();
+ checkBarModes();
}
@Override
- public void topAppWindowChanged(boolean showMenu) {
- if (mNavigationBarView != null) {
+ public void topAppWindowChanged(int displayId, boolean showMenu) {
+ if (displayId == mDisplayId && mNavigationBarView != null) {
mNavigationBarView.setMenuVisibility(showMenu);
}
}
@Override
- public void setWindowState(int window, int state) {
- if (mNavigationBarView != null
+ public void setWindowState(
+ int displayId, @WindowType int window, @WindowVisibleState int state) {
+ if (displayId == mDisplayId
+ && mNavigationBarView != null
&& window == StatusBarManager.WINDOW_NAVIGATION_BAR
&& mNavigationBarWindowState != state) {
mNavigationBarWindowState = state;
@@ -426,28 +446,30 @@
.onRotationProposal(rotation, winRotation, isValid);
}
- // Injected from StatusBar at creation.
- public void setCurrentSysuiVisibility(int systemUiVisibility) {
+ /**
+ * Sets System UI flags to {@link NavigationBarFragment}.
+ *
+ * @see View#setSystemUiVisibility(int)
+ */
+ public void setSystemUiVisibility(int systemUiVisibility) {
mSystemUiVisibility = systemUiVisibility;
- final int barMode = mStatusBar.computeBarMode(0, mSystemUiVisibility,
- View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
- View.NAVIGATION_BAR_TRANSPARENT);
+ final int barMode = computeBarMode(0, mSystemUiVisibility);
if (barMode != -1) {
mNavigationBarMode = barMode;
}
checkNavBarModes();
- mStatusBar.touchAutoHide();
+ mAutoHideController.touchAutoHide();
- // TODO(115978725): Support light bar controller on external nav bars.
- if (mLightBarController != null) {
- mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
- true /* nbModeChanged */, mNavigationBarMode);
- }
+ mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
+ true /* nbModeChanged */, mNavigationBarMode);
}
@Override
- public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
- int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+ public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+ int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+ if (displayId != mDisplayId) {
+ return;
+ }
final int oldVal = mSystemUiVisibility;
final int newVal = (oldVal & ~mask) | (vis & mask);
final int diff = newVal ^ oldVal;
@@ -457,9 +479,7 @@
// update navigation bar mode
final int nbMode = getView() == null
- ? -1 : mStatusBar.computeBarMode(oldVal, newVal,
- View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
- View.NAVIGATION_BAR_TRANSPARENT);
+ ? -1 : computeBarMode(oldVal, newVal);
nbModeChanged = nbMode != -1;
if (nbModeChanged) {
if (mNavigationBarMode != nbMode) {
@@ -470,19 +490,45 @@
mNavigationBarMode = nbMode;
checkNavBarModes();
}
- mStatusBar.touchAutoHide();
+ mAutoHideController.touchAutoHide();
}
}
+ mLightBarController.onNavigationVisibilityChanged(
+ vis, mask, nbModeChanged, mNavigationBarMode);
+ }
- // TODO(115978725): Support light bar controller on external nav bars.
- if (mLightBarController != null) {
- mLightBarController.onNavigationVisibilityChanged(vis, mask, nbModeChanged,
- mNavigationBarMode);
+ private @TransitionMode int computeBarMode(int oldVis, int newVis) {
+ final int oldMode = barMode(oldVis);
+ final int newMode = barMode(newVis);
+ if (oldMode == newMode) {
+ return -1; // no mode change
+ }
+ return newMode;
+ }
+
+ private @TransitionMode int barMode(int vis) {
+ final int lightsOutTransparent =
+ View.SYSTEM_UI_FLAG_LOW_PROFILE | View.NAVIGATION_BAR_TRANSIENT;
+ if ((vis & View.NAVIGATION_BAR_TRANSIENT) != 0) {
+ return MODE_SEMI_TRANSPARENT;
+ } else if ((vis & View.NAVIGATION_BAR_TRANSLUCENT) != 0) {
+ return MODE_TRANSLUCENT;
+ } else if ((vis & lightsOutTransparent) == lightsOutTransparent) {
+ return MODE_LIGHTS_OUT_TRANSPARENT;
+ } else if ((vis & View.NAVIGATION_BAR_TRANSPARENT) != 0) {
+ return MODE_TRANSPARENT;
+ } else if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
+ return MODE_LIGHTS_OUT;
+ } else {
+ return MODE_OPAQUE;
}
}
@Override
- public void disable(int state1, int state2, boolean animate) {
+ public void disable(int displayId, int state1, int state2, boolean animate) {
+ if (displayId != mDisplayId) {
+ return;
+ }
// Navigation bar flags are in both state1 and state2.
final int masked = state1 & (StatusBarManager.DISABLE_HOME
| StatusBarManager.DISABLE_RECENT
@@ -511,7 +557,7 @@
}
}
- // ----- Internal stuffz -----
+ // ----- Internal stuff -----
private void refreshLayout(int layoutDirection) {
if (mNavigationBarView != null) {
@@ -610,7 +656,7 @@
}
private boolean onNavigationTouch(View v, MotionEvent event) {
- mStatusBar.checkUserAutohide(event);
+ mAutoHideController.checkUserAutoHide(event);
return false;
}
@@ -800,30 +846,69 @@
mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection);
}
- // ----- Methods that StatusBar talks to (should be minimized) -----
+ // ----- Methods that DisplayNavigationBarController talks to -----
+
+ /** Applies auto dimming animation on navigation bar when touched. */
+ public void touchAutoDim() {
+ getBarTransitions().setAutoDim(false);
+ mHandler.removeCallbacks(mAutoDim);
+ int state = Dependency.get(StatusBarStateController.class).getState();
+ if (state != StatusBarState.KEYGUARD && state != StatusBarState.SHADE_LOCKED) {
+ mHandler.postDelayed(mAutoDim, AUTODIM_TIMEOUT_MS);
+ }
+ }
public void setLightBarController(LightBarController lightBarController) {
mLightBarController = lightBarController;
mLightBarController.setNavigationBar(mNavigationBarView.getLightTransitionsController());
}
+ /** Sets {@link AutoHideController} to the navigation bar. */
+ public void setAutoHideController(AutoHideController autoHideController) {
+ mAutoHideController = autoHideController;
+ mAutoHideController.setNavigationBar(this);
+ }
+
public boolean isSemiTransparent() {
return mNavigationBarMode == MODE_SEMI_TRANSPARENT;
}
+ private void checkBarModes() {
+ // We only have status bar on default display now.
+ if (mIsOnDefaultDisplay) {
+ mStatusBar.checkBarModes();
+ } else {
+ checkNavBarModes();
+ }
+ }
+
+ /**
+ * Checks current navigation bar mode and make transitions.
+ */
+ public void checkNavBarModes() {
+ final boolean anim = mStatusBar.isDeviceInteractive()
+ && mNavigationBarWindowState != WINDOW_STATE_HIDDEN;
+ mNavigationBarView.getBarTransitions().transitionTo(mNavigationBarMode, anim);
+ }
+
public void disableAnimationsDuringHide(long delay) {
mNavigationBarView.setLayoutTransitionsEnabled(false);
mNavigationBarView.postDelayed(() -> mNavigationBarView.setLayoutTransitionsEnabled(true),
delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
}
- public BarTransitions getBarTransitions() {
- return mNavigationBarView.getBarTransitions();
+ /**
+ * Performs transitions on navigation bar.
+ *
+ * @param barMode transition bar mode.
+ * @param animate shows animations if {@code true}.
+ */
+ public void transitionTo(@TransitionMode int barMode, boolean animate) {
+ getBarTransitions().transitionTo(barMode, animate);
}
- public void checkNavBarModes() {
- mStatusBar.checkBarMode(mNavigationBarMode,
- mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
+ private BarTransitions getBarTransitions() {
+ return mNavigationBarView.getBarTransitions();
}
public void finishBarAnimations() {
@@ -873,12 +958,11 @@
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
// The accessibility settings may be different for the new user
updateAccessibilityServicesState(mAccessibilityManager);
- };
+ }
}
};
public static View create(Context context, FragmentListener listener) {
- final int displayId = context.getDisplay().getDisplayId();
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
@@ -890,7 +974,7 @@
| WindowManager.LayoutParams.FLAG_SLIPPERY,
PixelFormat.TRANSLUCENT);
lp.token = new Binder();
- lp.setTitle("NavigationBar" + displayId);
+ lp.setTitle("NavigationBar" + context.getDisplayId());
lp.accessibilityTitle = context.getString(R.string.nav_bar);
lp.windowAnimations = 0;
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 8c17922..2fc7b78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -282,6 +282,11 @@
}
@Override
+ public void onHomeButtonVisibilityChanged(boolean visible) {
+ getHomeButton().setVisibility(visible ? VISIBLE : GONE);
+ }
+
+ @Override
public void onColorAdaptChanged(boolean enabled) {
if (enabled) {
mColorAdaptionController.start();
@@ -672,6 +677,7 @@
// TODO(b/113914868): investigation log for disappearing home button
Log.i(TAG, "updateNavButtonIcons (b/113914868): home disabled=" + disableHome
+ " mDisabledFlags=" + mDisabledFlags);
+ disableHome |= mPrototypeController.hideHomeButton();
// Always disable recents when alternate car mode UI is active and for secondary displays.
boolean disableRecent = isRecentsButtonDisabled();
@@ -945,6 +951,7 @@
// TODO(b/112934365): remove after prototype finished, only needed to escape from pin
getBackButton().setVisibility(VISIBLE);
+ getHomeButton().setVisibility(VISIBLE);
} else {
mScreenPinningNotify.showPinningExitToast();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
index 40ac793..fb6254b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -34,6 +34,7 @@
*/
public class NavigationPrototypeController extends ContentObserver {
private static final String HIDE_BACK_BUTTON_SETTING = "quickstepcontroller_hideback";
+ private static final String HIDE_HOME_BUTTON_SETTING = "quickstepcontroller_hidehome";
static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
@@ -73,6 +74,7 @@
*/
public void register() {
registerObserver(HIDE_BACK_BUTTON_SETTING);
+ registerObserver(HIDE_HOME_BUTTON_SETTING);
registerObserver(GESTURE_MATCH_SETTING);
registerObserver(NAV_COLOR_ADAPT_ENABLE_SETTING);
}
@@ -88,22 +90,20 @@
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
if (!selfChange && mListener != null) {
- try {
- final String path = uri.getPath();
- if (path.endsWith(GESTURE_MATCH_SETTING)) {
- // Get the settings gesture map corresponding to each action
- // {@see updateSwipeLTRBackSetting}
- updateSwipeLTRBackSetting();
- mListener.onGestureRemap(mActionMap);
- } else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) {
- mListener.onBackButtonVisibilityChanged(
- !getGlobalBool(HIDE_BACK_BUTTON_SETTING));
- } else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) {
- mListener.onColorAdaptChanged(
- NavBarTintController.isEnabled(mContext));
- }
- } catch (SettingNotFoundException e) {
- e.printStackTrace();
+ final String path = uri.getPath();
+ if (path.endsWith(GESTURE_MATCH_SETTING)) {
+ // Get the settings gesture map corresponding to each action
+ // {@see updateSwipeLTRBackSetting}
+ updateSwipeLTRBackSetting();
+ mListener.onGestureRemap(mActionMap);
+ } else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) {
+ mListener.onBackButtonVisibilityChanged(
+ !getGlobalBool(HIDE_BACK_BUTTON_SETTING, false));
+ } else if (path.endsWith(HIDE_HOME_BUTTON_SETTING)) {
+ mListener.onHomeButtonVisibilityChanged(!hideHomeButton());
+ } else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) {
+ mListener.onColorAdaptChanged(
+ NavBarTintController.isEnabled(mContext));
}
}
}
@@ -117,6 +117,13 @@
}
/**
+ * @return if home button should be invisible
+ */
+ boolean hideHomeButton() {
+ return getGlobalBool(HIDE_HOME_BUTTON_SETTING, false /* default */);
+ }
+
+ /**
* Since Settings.Global cannot pass arrays, use a string to represent each character as a
* gesture map to actions corresponding to {@see GestureAction}. The number is represented as:
* Number: [up] [down] [left] [right]
@@ -131,8 +138,8 @@
}
}
- private boolean getGlobalBool(String name) throws SettingNotFoundException {
- return Settings.Global.getInt(mContext.getContentResolver(), name) == 1;
+ private boolean getGlobalBool(String name, boolean defaultVal) {
+ return Settings.Global.getInt(mContext.getContentResolver(), name, defaultVal ? 1 : 0) == 1;
}
private void registerObserver(String name) {
@@ -143,6 +150,7 @@
public interface OnPrototypeChangedListener {
void onGestureRemap(@GestureAction int[] actions);
void onBackButtonVisibilityChanged(boolean visible);
+ void onHomeButtonVisibilityChanged(boolean visible);
void onColorAdaptChanged(boolean enabled);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index e40835f..056c8a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -19,14 +19,14 @@
import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.tuner.TunerService;
import java.util.ArrayList;
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 1b18c6c..16576ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -312,6 +312,7 @@
Dependency.get(NotificationLockscreenUserManager.class);
private final ShadeController mShadeController =
Dependency.get(ShadeController.class);
+ private int mDisplayId;
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -323,6 +324,7 @@
mAlphaPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
setPanelAlpha(255, false /* animate */);
mCommandQueue = getComponent(context, CommandQueue.class);
+ mDisplayId = context.getDisplayId();
}
private void setStatusBar(StatusBar bar) {
@@ -2596,7 +2598,7 @@
}
if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
mShowIconsWhenExpanded = showIconsWhenExpanded;
- mCommandQueue.recomputeDisableFlags(false);
+ mCommandQueue.recomputeDisableFlags(mDisplayId, false);
}
}
@@ -2803,6 +2805,11 @@
if (animatePulse) {
mAnimateNextPositionUpdate = true;
}
+ // Do not animate the clock when waking up from a pulse.
+ // The height callback will take care of pushing the clock to the right position.
+ if (!mPulsing && !mDozing) {
+ mAnimateNextPositionUpdate = false;
+ }
mNotificationStackScroller.setPulsing(pulsing, animatePulse);
mKeyguardStatusView.setPulsing(pulsing, animatePulse);
mKeyguardBottomArea.setPulsing(pulsing, animatePulse);
@@ -2856,7 +2863,7 @@
if (hideIcons != mHideIconsDuringNotificationLaunch) {
mHideIconsDuringNotificationLaunch = hideIcons;
if (!hideIcons) {
- mCommandQueue.recomputeDisableFlags(true /* animate */);
+ mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index d6f2fd7..43c35f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -736,9 +736,12 @@
};
@Override
- public void appTransitionStarting(long startTime, long duration, boolean forced) {
- updateManagedProfile();
- updateForegroundInstantApps();
+ public void appTransitionStarting(int displayId, long startTime, long duration,
+ boolean forced) {
+ if (mContext.getDisplayId() == displayId) {
+ updateManagedProfile();
+ updateForegroundInstantApps();
+ }
}
@Override
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 2129835..7a3d03f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -43,9 +43,9 @@
import com.android.systemui.Dependency;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
import java.util.Objects;
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 e1a6cd3..b233018 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -20,9 +20,10 @@
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static android.app.StatusBarManager.WindowType;
+import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
@@ -37,6 +38,7 @@
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
+import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -71,7 +73,6 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
import android.media.AudioAttributes;
import android.metrics.LogMaker;
import android.net.Uri;
@@ -155,6 +156,8 @@
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.qs.QSFragment;
@@ -168,11 +171,11 @@
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.DisplayNavigationBarController;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyboardShortcuts;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -204,7 +207,6 @@
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.ExtensionController;
@@ -288,10 +290,6 @@
protected static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
- private static final int STATUS_OR_NAV_TRANSIENT =
- View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
- private static final long AUTOHIDE_TIMEOUT_MS = 2250;
-
/**
* The delay to reset the hint text when the hint animation is finished running.
*/
@@ -340,6 +338,8 @@
protected BiometricUnlockController mBiometricUnlockController;
private LightBarController mLightBarController;
protected LockscreenWallpaper mLockscreenWallpaper;
+ @VisibleForTesting
+ protected AutoHideController mAutoHideController;
private int mNaturalBarHeight = -1;
@@ -404,9 +404,6 @@
private final Rect mLastFullscreenStackBounds = new Rect();
private final Rect mLastDockedStackBounds = new Rect();
- // last value sent to window manager
- private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
-
private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class);
// XXX: gesture research
@@ -446,21 +443,13 @@
protected final H mHandler = createHandler();
private int mInteractingWindows;
- private boolean mAutohideSuspended;
- private int mStatusBarMode;
+ private @TransitionMode int mStatusBarMode;
private ViewMediatorCallback mKeyguardViewMediatorCallback;
protected ScrimController mScrimController;
protected DozeScrimController mDozeScrimController;
private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
- private final Runnable mAutohide = () -> {
- int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT;
- if (mSystemUiVisibility != requested) {
- notifyUiVisibilityChanged(requested);
- }
- };
-
protected boolean mDozing;
private boolean mDozingRequested;
@@ -584,11 +573,6 @@
}
};
- protected DisplayManager mDisplayManager;
-
- private NavigationBarFragment mNavigationBar;
- private View mNavigationBarView;
-
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private boolean mVibrateOnOpening;
private VibratorHelper mVibratorHelper;
@@ -642,7 +626,7 @@
mKeyguardViewMediator = getComponent(KeyguardViewMediator.class);
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
- mNavigationBarController = Dependency.get(DisplayNavigationBarController.class);
+ mNavigationBarController = Dependency.get(NavigationBarController.class);
mBubbleController = Dependency.get(BubbleController.class);
mBubbleController.setExpandListener(mBubbleExpandListener);
KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance();
@@ -660,6 +644,7 @@
ServiceManager.checkService(DreamService.DREAM_SERVICE));
mDisplay = mWindowManager.getDefaultDisplay();
+ mDisplayId = mDisplay.getDisplayId();
updateDisplaySize();
Resources res = mContext.getResources();
@@ -715,11 +700,11 @@
// Set up the initial notification state. This needs to happen before CommandQueue.disable()
setUpPresenter();
- setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
+ setSystemUiVisibility(mDisplayId, switches[1], switches[7], switches[8], 0xffffffff,
fullscreenStackBounds, dockedStackBounds);
- topAppWindowChanged(switches[2] != 0);
+ topAppWindowChanged(mDisplayId, switches[2] != 0);
// StatusBarManagerService has a back up of IME token and it's restored here.
- setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
+ setImeWindowStatus(mDisplayId, binders.get(0), switches[3], switches[4], switches[5] != 0);
// Set up the initial icon state
int N = iconSlots.size();
@@ -808,6 +793,9 @@
mNotificationIconAreaController.setupShelf(mNotificationShelf);
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController);
+ // Allow plugins to reference DarkIconDispatcher
+ Dependency.get(PluginDependencyProvider.class)
+ .allowPluginDependency(DarkIconDispatcher.class);
FragmentHostManager.get(mStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
CollapsedStatusBarFragment statusBarFragment =
@@ -903,11 +891,10 @@
}
});
- // TODO(115978725): Support light bar controller on external nav bars.
+ mAutoHideController = Dependency.get(AutoHideController.class);
+ mAutoHideController.setStatusBar(this);
+
mLightBarController = Dependency.get(LightBarController.class);
- if (mNavigationBar != null) {
- mNavigationBar.setLightBarController(mLightBarController);
- }
ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind);
ScrimView scrimInFront = mStatusBarWindow.findViewById(R.id.scrim_in_front);
@@ -1066,7 +1053,7 @@
* @param state2 disable2 flags
*/
protected void setUpDisableFlags(int state1, int state2) {
- mCommandQueue.disable(state1, state2, false /* animate */);
+ mCommandQueue.disable(mDisplayId, state1, state2, false /* animate */);
}
@Override
@@ -1093,25 +1080,10 @@
}
}
+ // TODO(b/117478341): This was left such that CarStatusBar can override this method.
+ // Try to remove this.
protected void createNavigationBar() {
- try {
- // TODO(117478341): Move this into DisplayNavigationBarController#createNavigationBars
- // for-loop. We will also move the whole navigation bar logic together.
- final boolean showNav = mWindowManagerService.hasNavigationBar(DEFAULT_DISPLAY);
- if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
- if (!showNav) return;
-
- mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
- mNavigationBar = (NavigationBarFragment) fragment;
- if (mLightBarController != null) {
- mNavigationBar.setLightBarController(mLightBarController);
- }
- mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
- });
- } catch (RemoteException ex) {
- // no window manager? good luck with that
- }
- mNavigationBarController.createNavigationBars();
+ mNavigationBarController.createNavigationBars(true /* includeDefaultDisplay */);
}
/**
@@ -1120,7 +1092,7 @@
*/
protected View.OnTouchListener getStatusBarWindowTouchListener() {
return (v, event) -> {
- checkUserAutohide(event);
+ mAutoHideController.checkUserAutoHide(event);
mRemoteInputManager.checkRemoteInputOutside(event);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (mExpandedVisible) {
@@ -1255,8 +1227,7 @@
}
int dockSide = WindowManagerProxy.getInstance().getDockSide();
if (dockSide == WindowManager.DOCKED_INVALID) {
- final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(
- mDisplay.getDisplayId());
+ final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(mDisplayId);
if (navbarPos == NAV_BAR_POS_INVALID) {
return false;
}
@@ -1365,7 +1336,10 @@
* State is one or more of the DISABLE constants from StatusBarManager.
*/
@Override
- public void disable(int state1, int state2, boolean animate) {
+ public void disable(int displayId, int state1, int state2, boolean animate) {
+ if (displayId != mDisplayId) {
+ return;
+ }
state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN;
@@ -1643,10 +1617,10 @@
mWereIconsJustHidden = true;
mHandler.postDelayed(() -> {
mWereIconsJustHidden = false;
- mCommandQueue.recomputeDisableFlags(true);
+ mCommandQueue.recomputeDisableFlags(mDisplayId, true);
}, 500);
} else {
- mCommandQueue.recomputeDisableFlags(animate);
+ mCommandQueue.recomputeDisableFlags(mDisplayId, animate);
}
}
if (shouldHideIconsForBouncer) {
@@ -1812,7 +1786,7 @@
mStatusBarWindowController.setPanelVisible(true);
visibilityChanged(true);
- mCommandQueue.recomputeDisableFlags(!force /* animate */);
+ mCommandQueue.recomputeDisableFlags(mDisplayId, !force /* animate */);
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
}
@@ -1985,7 +1959,7 @@
Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen");
}
mCommandQueue.recomputeDisableFlags(
- mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */);
+ mDisplayId, mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */);
// Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
// the bouncer appear animation.
@@ -2042,7 +2016,11 @@
}
@Override // CommandQueue
- public void setWindowState(int window, int state) {
+ public void setWindowState(
+ int displayId, @WindowType int window, @WindowVisibleState int state) {
+ if (displayId != mDisplayId) {
+ return;
+ }
boolean showing = state == WINDOW_STATE_SHOWING;
if (mStatusBarWindow != null
&& window == StatusBarManager.WINDOW_STATUS_BAR
@@ -2061,14 +2039,17 @@
}
@Override // CommandQueue
- public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
- int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+ public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+ int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+ if (displayId != mDisplayId) {
+ return;
+ }
final int oldVal = mSystemUiVisibility;
final int newVal = (oldVal&~mask) | (vis&mask);
final int diff = newVal ^ oldVal;
if (DEBUG) Log.d(TAG, String.format(
- "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
- Integer.toHexString(vis), Integer.toHexString(mask),
+ "setSystemUiVisibility displayId=%d vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
+ displayId, Integer.toHexString(vis), Integer.toHexString(mask),
Integer.toHexString(oldVal), Integer.toHexString(newVal),
Integer.toHexString(diff)));
boolean sbModeChanged = false;
@@ -2082,7 +2063,6 @@
// ready to unhide
if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
- mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
mNoAnimationOnNextBarModeChange = true;
}
@@ -2093,17 +2073,9 @@
if (sbModeChanged && sbMode != mStatusBarMode) {
mStatusBarMode = sbMode;
checkBarModes();
- touchAutoHide();
+ mAutoHideController.touchAutoHide();
}
-
- if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
- mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
- }
-
- // send updated sysui visibility to window manager
- notifyUiVisibilityChanged(mSystemUiVisibility);
}
-
mLightBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis,
mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode);
}
@@ -2131,52 +2103,45 @@
}
}
- // TODO(115978725): Support auto hide on external nav bars.
- void touchAutoHide() {
- // update transient bar autohide
- if (mStatusBarMode == MODE_SEMI_TRANSPARENT || (mNavigationBar != null
- && mNavigationBar.isSemiTransparent())) {
- scheduleAutohide();
- } else {
- cancelAutohide();
- }
- }
-
- protected int computeStatusBarMode(int oldVal, int newVal) {
- return computeBarMode(oldVal, newVal, View.STATUS_BAR_TRANSIENT,
- View.STATUS_BAR_TRANSLUCENT, View.STATUS_BAR_TRANSPARENT);
+ protected @TransitionMode int computeStatusBarMode(int oldVal, int newVal) {
+ return computeBarMode(oldVal, newVal);
}
protected BarTransitions getStatusBarTransitions() {
return mStatusBarView.getBarTransitions();
}
- protected int computeBarMode(int oldVis, int newVis,
- int transientFlag, int translucentFlag, int transparentFlag) {
- final int oldMode = barMode(oldVis, transientFlag, translucentFlag, transparentFlag);
- final int newMode = barMode(newVis, transientFlag, translucentFlag, transparentFlag);
+ protected @TransitionMode int computeBarMode(int oldVis, int newVis) {
+ final int oldMode = barMode(oldVis);
+ final int newMode = barMode(newVis);
if (oldMode == newMode) {
return -1; // no mode change
}
return newMode;
}
- private int barMode(int vis, int transientFlag, int translucentFlag, int transparentFlag) {
- int lightsOutTransparent = View.SYSTEM_UI_FLAG_LOW_PROFILE | transparentFlag;
- return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT
- : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT
- : (vis & lightsOutTransparent) == lightsOutTransparent ? MODE_LIGHTS_OUT_TRANSPARENT
- : (vis & transparentFlag) != 0 ? MODE_TRANSPARENT
- : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT
- : MODE_OPAQUE;
+ private @TransitionMode int barMode(int vis) {
+ int lightsOutTransparent = View.SYSTEM_UI_FLAG_LOW_PROFILE | View.STATUS_BAR_TRANSPARENT;
+ if ((vis & View.STATUS_BAR_TRANSIENT) != 0) {
+ return MODE_SEMI_TRANSPARENT;
+ } else if ((vis & View.STATUS_BAR_TRANSLUCENT) != 0) {
+ return MODE_TRANSLUCENT;
+ } else if ((vis & lightsOutTransparent) == lightsOutTransparent) {
+ return MODE_LIGHTS_OUT_TRANSPARENT;
+ } else if ((vis & View.STATUS_BAR_TRANSPARENT) != 0) {
+ return MODE_TRANSPARENT;
+ } else if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
+ return MODE_LIGHTS_OUT;
+ } else {
+ return MODE_OPAQUE;
+ }
}
- // TODO(115978725): Support animations on external nav bars.
void checkBarModes() {
if (mDemoMode) return;
if (mStatusBarView != null) checkBarMode(mStatusBarMode, mStatusBarWindowState,
getStatusBarTransitions());
- if (mNavigationBar != null) mNavigationBar.checkNavBarModes();
+ mNavigationBarController.checkNavBarModes(mDisplayId);
mNoAnimationOnNextBarModeChange = false;
}
@@ -2185,20 +2150,18 @@
mNotificationPanel.setQsScrimEnabled(scrimEnabled);
}
- void checkBarMode(int mode, int windowState, BarTransitions transitions) {
+ void checkBarMode(@TransitionMode int mode, @WindowVisibleState int windowState,
+ BarTransitions transitions) {
final boolean anim = !mNoAnimationOnNextBarModeChange && mDeviceInteractive
&& windowState != WINDOW_STATE_HIDDEN;
transitions.transitionTo(mode, anim);
}
- // TODO(115978725): Support animations on external nav bars.
private void finishBarAnimations() {
if (mStatusBarView != null) {
mStatusBarView.getBarTransitions().finishAnimations();
}
- if (mNavigationBar != null) {
- mNavigationBar.finishBarAnimations();
- }
+ mNavigationBarController.finishBarAnimations(mDisplayId);
}
private final Runnable mCheckBarModes = this::checkBarModes;
@@ -2209,13 +2172,13 @@
? (mInteractingWindows | barWindow)
: (mInteractingWindows & ~barWindow);
if (mInteractingWindows != 0) {
- suspendAutohide();
+ mAutoHideController.suspendAutoHide();
} else {
- resumeSuspendedAutohide();
+ mAutoHideController.resumeSuspendedAutoHide();
}
// manually dismiss the volume panel when interacting with the nav bar
if (changing && interacting && barWindow == StatusBarManager.WINDOW_NAVIGATION_BAR) {
- touchAutoDim();
+ mNavigationBarController.touchAutoDim(mDisplayId);
dismissVolumeDialog();
}
checkBarModes();
@@ -2227,59 +2190,6 @@
}
}
- private void resumeSuspendedAutohide() {
- if (mAutohideSuspended) {
- scheduleAutohide();
- mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher
- }
- }
-
- private void suspendAutohide() {
- mHandler.removeCallbacks(mAutohide);
- mHandler.removeCallbacks(mCheckBarModes);
- mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0;
- }
-
- private void cancelAutohide() {
- mAutohideSuspended = false;
- mHandler.removeCallbacks(mAutohide);
- }
-
- private void scheduleAutohide() {
- cancelAutohide();
- mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS);
- }
-
- public void touchAutoDim() {
- if (mNavigationBar != null) {
- mNavigationBar.getBarTransitions().setAutoDim(false);
- }
- mHandler.removeCallbacks(mAutoDim);
-
- // Do not dim the navigation buttons if the its tint is controlled by the bar's background
- if (NavBarTintController.isEnabled(mContext)) {
- return;
- }
- if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
- mHandler.postDelayed(mAutoDim, AUTOHIDE_TIMEOUT_MS);
- }
- }
-
- void checkUserAutohide(MotionEvent event) {
- if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0 // a transient bar is revealed
- && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
- && event.getX() == 0 && event.getY() == 0 // a touch outside both bars
- && !mRemoteInputManager.getController()
- .isRemoteInputActive()) { // not due to typing in IME
- userAutohide();
- }
- }
-
- private void userAutohide() {
- cancelAutohide();
- mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear
- }
-
private boolean areLightsOn() {
return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
}
@@ -2287,30 +2197,21 @@
public void setLightsOn(boolean on) {
Log.v(TAG, "setLightsOn(" + on + ")");
if (on) {
- setSystemUiVisibility(0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
+ setSystemUiVisibility(mDisplayId, 0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
mLastFullscreenStackBounds, mLastDockedStackBounds);
} else {
- setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0,
+ setSystemUiVisibility(mDisplayId, View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0,
View.SYSTEM_UI_FLAG_LOW_PROFILE, mLastFullscreenStackBounds,
mLastDockedStackBounds);
}
}
- private void notifyUiVisibilityChanged(int vis) {
- try {
- if (mLastDispatchedSystemUiVisibility != vis) {
- // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
- mWindowManagerService.statusBarVisibilityChanged(Display.DEFAULT_DISPLAY, vis);
- mLastDispatchedSystemUiVisibility = vis;
- }
- } catch (RemoteException ex) {
- }
- }
-
@Override
- public void topAppWindowChanged(boolean showMenu) {
+ public void topAppWindowChanged(int displayId, boolean showMenu) {
+ if (mDisplayId != displayId) return;
if (SPEW) {
- Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
+ Log.d(TAG, "display#" + displayId + ": "
+ + (showMenu ? "showing" : "hiding") + " the MENU button");
}
// See above re: lights-out policy for legacy apps.
@@ -2925,10 +2826,6 @@
mWindowManager.removeViewImmediate(mStatusBarWindow);
mStatusBarWindow = null;
}
- if (mNavigationBarView != null) {
- mWindowManager.removeViewImmediate(mNavigationBarView);
- mNavigationBarView = null;
- }
mNavigationBarController.destroy();
mContext.unregisterReceiver(mBroadcastReceiver);
mContext.unregisterReceiver(mDemoReceiver);
@@ -2998,15 +2895,12 @@
"transparent".equals(mode) ? MODE_TRANSPARENT :
"warning".equals(mode) ? MODE_WARNING :
-1;
- // TODO(115978725): Support external nav bar transitions
if (barMode != -1) {
boolean animate = true;
if (mStatusBarView != null) {
mStatusBarView.getBarTransitions().transitionTo(barMode, animate);
}
- if (mNavigationBar != null) {
- mNavigationBar.getBarTransitions().transitionTo(barMode, animate);
- }
+ mNavigationBarController.transitionTo(mDisplayId, barMode, animate);
}
}
if (modeChange || command.equals(COMMAND_OPERATOR)) {
@@ -3144,7 +3038,7 @@
.setDuration(FADE_KEYGUARD_DURATION)
.withLayer()
.withEndAction(this::onLaunchTransitionFadingEnded);
- mCommandQueue.appTransitionStarting(SystemClock.uptimeMillis(),
+ mCommandQueue.appTransitionStarting(mDisplayId, SystemClock.uptimeMillis(),
LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
};
if (mNotificationPanel.isLaunchTransitionRunning()) {
@@ -3232,12 +3126,9 @@
mDraggedDownEntry = null;
}
- // TODO(115978725): Support animations on external nav bars.
// Disable layout transitions in navbar for this transition because the load is just
// too heavy for the CPU and GPU on any device.
- if (mNavigationBar != null) {
- mNavigationBar.disableAnimationsDuringHide(delay);
- }
+ mNavigationBarController.disableAnimationsDuringHide(mDisplayId, delay);
} else if (!mNotificationPanel.isCollapsing()) {
instantCollapseNotificationPanel();
}
@@ -3270,7 +3161,7 @@
// Treat Keyguard exit animation as an app transition to achieve nice transition for status
// bar.
mKeyguardMonitor.notifyKeyguardGoingAway(true);
- mCommandQueue.appTransitionPending(true);
+ mCommandQueue.appTransitionPending(mDisplayId, true /* forced */);
}
/**
@@ -3281,11 +3172,11 @@
* @param fadeoutDuration the duration of the exit animation, in milliseconds
*/
public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration) {
- mCommandQueue.appTransitionStarting(startTime + fadeoutDuration
+ mCommandQueue.appTransitionStarting(mDisplayId, startTime + fadeoutDuration
- LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
- mCommandQueue.recomputeDisableFlags(fadeoutDuration > 0 /* animate */);
- mCommandQueue.appTransitionStarting(
+ mCommandQueue.recomputeDisableFlags(mDisplayId, fadeoutDuration > 0 /* animate */);
+ mCommandQueue.appTransitionStarting(mDisplayId,
startTime - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
mKeyguardMonitor.notifyKeyguardFadingAway(delay, fadeoutDuration);
@@ -3411,7 +3302,7 @@
// Make our window larger and the panel expanded.
makeExpandedVisible(true);
mNotificationPanel.expand(false /* animate */);
- mCommandQueue.recomputeDisableFlags(false /* animate */);
+ mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */);
}
@Override
@@ -3467,7 +3358,7 @@
updateReportRejectedTouchVisibility();
updateDozing();
updateTheme();
- touchAutoDim();
+ mNavigationBarController.touchAutoDim(mDisplayId);
Trace.beginSection("StatusBar#updateKeyguardState");
if (mState == StatusBarState.KEYGUARD) {
mKeyguardIndicationController.setVisible(true);
@@ -3588,11 +3479,7 @@
// TODO: Figure out way to remove these.
public NavigationBarView getNavigationBarView() {
- return (mNavigationBar != null ? (NavigationBarView) mNavigationBar.getView() : null);
- }
-
- public View getNavigationBarWindow() {
- return mNavigationBarView;
+ return mNavigationBarController.getDefaultNavigationBarView();
}
/**
@@ -3656,7 +3543,7 @@
mBouncerShowing = bouncerShowing;
if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing);
updateHideIconsForBouncer(true /* animate */);
- mCommandQueue.recomputeDisableFlags(true /* animate */);
+ mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
updateScrimController();
}
@@ -3781,13 +3668,17 @@
}
@Override
- public void appTransitionCancelled() {
- getComponent(Divider.class).onAppTransitionFinished();
+ public void appTransitionCancelled(int displayId) {
+ if (displayId == mDisplayId) {
+ getComponent(Divider.class).onAppTransitionFinished();
+ }
}
@Override
- public void appTransitionFinished() {
- getComponent(Divider.class).onAppTransitionFinished();
+ public void appTransitionFinished(int displayId) {
+ if (displayId == mDisplayId) {
+ getComponent(Divider.class).onAppTransitionFinished();
+ }
}
@Override
@@ -4174,7 +4065,7 @@
private DeviceProvisionedController mDeviceProvisionedController
= Dependency.get(DeviceProvisionedController.class);
- protected DisplayNavigationBarController mNavigationBarController;
+ protected NavigationBarController mNavigationBarController;
// UI-specific methods
@@ -4183,6 +4074,7 @@
private IDreamManager mDreamManager;
protected Display mDisplay;
+ private int mDisplayId;
protected Recents mRecents;
@@ -4310,7 +4202,7 @@
// Immediately update the icon hidden state, since that should only apply if we're
// staying fullscreen.
mWereIconsJustHidden = false;
- mCommandQueue.recomputeDisableFlags(true);
+ mCommandQueue.recomputeDisableFlags(mDisplayId, true);
}
updateHideIconsForBouncer(true /* animate */);
}
@@ -4502,13 +4394,6 @@
}
// End Extra BaseStatusBarMethods.
- // TODO(115978725): Handle dimming for external nav bars
- private final Runnable mAutoDim = () -> {
- if (mNavigationBar != null) {
- mNavigationBar.getBarTransitions().setAutoDim(true);
- }
- };
-
public NotificationGutsManager getGutsManager() {
return mGutsManager;
}
@@ -4517,4 +4402,8 @@
public interface StatusBarInjector {
void createStatusBar(StatusBar statusbar);
}
+
+ public @TransitionMode int getStatusBarMode() {
+ return mStatusBarMode;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 26c9d28..2e2ff1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -38,14 +38,14 @@
import com.android.systemui.DemoMode;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.StatusBarWifiView;
import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.util.Utils.DisableStateTracker;
import java.util.List;
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 116ecc8..3ddfc0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -238,8 +238,10 @@
}
@Override
- public void disable(int state1, int state2, boolean animate) {
- mDisabled2 = state2;
+ public void disable(int displayId, int state1, int state2, boolean animate) {
+ if (displayId == mContext.getDisplayId()) {
+ mDisabled2 = state2;
+ }
}
protected class ChallengeReceiver extends BroadcastReceiver {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SysuiDarkIconDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SysuiDarkIconDispatcher.java
new file mode 100644
index 0000000..d537721
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SysuiDarkIconDispatcher.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.plugins.DarkIconDispatcher;
+
+/**
+ * Dispatches events to {@link DarkReceiver}s about changes in darkness, tint area
+ * and dark intensity.
+ */
+public interface SysuiDarkIconDispatcher extends DarkIconDispatcher, Dumpable {
+
+ /**
+ * @return LightBarTransitionsController
+ */
+ LightBarTransitionsController getTransitionsController();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index aafdcd5..de7ef3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -44,11 +44,12 @@
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -298,7 +299,10 @@
}
@Override
- public void disable(int state1, int state2, boolean animate) {
+ public void disable(int displayId, int state1, int state2, boolean animate) {
+ if (displayId != getDisplay().getDisplayId()) {
+ return;
+ }
boolean clockVisibleByPolicy = (state1 & StatusBarManager.DISABLE_CLOCK) == 0;
if (clockVisibleByPolicy != mClockVisibleByPolicy) {
setClockVisibilityByPolicy(clockVisibleByPolicy);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java
deleted file mode 100644
index 0823db9..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.view.View;
-import android.widget.ImageView;
-
-import com.android.systemui.Dumpable;
-import com.android.systemui.statusbar.phone.LightBarTransitionsController;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Dispatches events to {@link DarkReceiver}s about changes in darkness, tint area and dark
- * intensity
- */
-public interface DarkIconDispatcher extends Dumpable {
-
- void setIconsDarkArea(Rect r);
- LightBarTransitionsController getTransitionsController();
-
- void addDarkReceiver(DarkReceiver receiver);
- void addDarkReceiver(ImageView imageView);
-
- // Must have been previously been added through one of the addDarkReceive methods above.
- void removeDarkReceiver(DarkReceiver object);
- void removeDarkReceiver(ImageView object);
-
- // Used to reapply darkness on an object, must have previously been added through
- // addDarkReceiver.
- void applyDark(DarkReceiver object);
-
- /**
- * Dumpable interface
- */
- default void dump(FileDescriptor fd, PrintWriter pw, String[] args) {}
-
- int DEFAULT_ICON_TINT = Color.WHITE;
- Rect sTmpRect = new Rect();
- int[] sTmpInt2 = new int[2];
-
- /**
- * @return the tint to apply to {@param view} depending on the desired tint {@param color} and
- * the screen {@param tintArea} in which to apply that tint
- */
- static int getTint(Rect tintArea, View view, int color) {
- if (isInArea(tintArea, view)) {
- return color;
- } else {
- return DEFAULT_ICON_TINT;
- }
- }
-
- /**
- * @return the dark intensity to apply to {@param view} depending on the desired dark
- * {@param intensity} and the screen {@param tintArea} in which to apply that intensity
- */
- static float getDarkIntensity(Rect tintArea, View view, float intensity) {
- if (isInArea(tintArea, view)) {
- return intensity;
- } else {
- return 0f;
- }
- }
-
- /**
- * @return true if more than half of the {@param view} area are in {@param area}, false
- * otherwise
- */
- static boolean isInArea(Rect area, View view) {
- if (area.isEmpty()) {
- return true;
- }
- sTmpRect.set(area);
- view.getLocationOnScreen(sTmpInt2);
- int left = sTmpInt2[0];
-
- int intersectStart = Math.max(left, area.left);
- int intersectEnd = Math.min(left + view.getWidth(), area.right);
- int intersectAmount = Math.max(0, intersectEnd - intersectStart);
-
- boolean coversFullStatusBar = area.top <= 0;
- boolean majorityOfWidth = 2 * intersectAmount > view.getWidth();
- return majorityOfWidth && coversFullStatusBar;
- }
-
- interface DarkReceiver {
- void onDarkChanged(Rect area, float darkIntensity, int tint);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
index 4a11754..54502e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
@@ -24,10 +24,10 @@
import android.view.MotionEvent;
import android.view.Surface;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.phone.NavigationBarView;
-import com.android.systemui.statusbar.phone.StatusBar;
/**
* The "dead zone" consumes unintentional taps along the top edge of the navigation bar.
@@ -44,7 +44,7 @@
public static final int VERTICAL = 1; // Consume taps along the left edge.
private static final boolean CHATTY = true; // print to logcat when we eat a click
- private final StatusBar mStatusBar;
+ private final NavigationBarController mNavBarController;
private final NavigationBarView mNavigationBarView;
private boolean mShouldFlash;
@@ -58,6 +58,7 @@
private boolean mVertical;
private long mLastPokeTime;
private int mDisplayRotation;
+ private final int mDisplayId;
private final Runnable mDebugFlash = new Runnable() {
@Override
@@ -68,8 +69,8 @@
public DeadZone(NavigationBarView view) {
mNavigationBarView = view;
- mStatusBar = SysUiServiceProvider.getComponent(mNavigationBarView.getContext(),
- StatusBar.class);
+ mNavBarController = Dependency.get(NavigationBarController.class);
+ mDisplayId = view.getContext().getDisplayId();
onConfigurationChanged(HORIZONTAL);
}
@@ -133,7 +134,7 @@
if (DEBUG) {
Slog.v(TAG, this + " ACTION_DOWN: " + event.getX() + "," + event.getY());
}
- if (mStatusBar != null) mStatusBar.touchAutoDim();
+ mNavBarController.touchAutoDim(mDisplayId);
int size = (int) getSize(event.getEventTime());
// In the vertical orientation consume taps along the left edge.
// In horizontal orientation consume taps along the top edge.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 2c756ce..d404982 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -33,6 +33,7 @@
import android.os.Bundle;
import android.os.SystemClock;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
@@ -304,6 +305,10 @@
.setSubtype(mCode)
.addTaggedData(MetricsEvent.FIELD_NAV_ACTION, action)
.addTaggedData(MetricsEvent.FIELD_FLAGS, flags));
+ // TODO(b/122195391): Added logs to make sure sysui is sending back button events
+ if (mCode == KeyEvent.KEYCODE_BACK && flags != KeyEvent.FLAG_LONG_PRESS) {
+ Log.i(TAG, "Back button event: " + KeyEvent.actionToString(action));
+ }
final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
index 630bd18..2b60274 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
@@ -82,6 +82,6 @@
* to modify the disable flags according to the status of mRemoteInputActive and misLandscape.
*/
private void recomputeDisableFlags() {
- mCommandQueue.recomputeDisableFlags(true);
+ mCommandQueue.recomputeDisableFlags(mContext.getDisplayId(), true);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 490cdd5c..6b4f7ed 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -69,10 +69,13 @@
/**
* Sets visibility of this {@link View} given the states passed from
- * {@link com.android.systemui.statusbar.CommandQueue.Callbacks#disable(int, int)}.
+ * {@link com.android.systemui.statusbar.CommandQueue.Callbacks#disable(int, int, int)}.
*/
@Override
- public void disable(int state1, int state2, boolean animate) {
+ public void disable(int displayId, int state1, int state2, boolean animate) {
+ if (displayId != mView.getDisplay().getDisplayId()) {
+ return;
+ }
final boolean disabled = ((state1 & mMask1) != 0) || ((state2 & mMask2) != 0);
if (disabled == mDisabled) return;
mDisabled = disabled;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
index 3426e11..61bfa75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
@@ -69,4 +69,10 @@
mDependency.onConfigurationChanged(null);
verify(d).onConfigurationChanged(eq(null));
}
+
+ @Test
+ public void testInitDependency() {
+ Dependency.clearDependencies();
+ Dependency.initDependencies(mContext);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index b84f85b..e91a7e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -16,13 +16,13 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
-import static com.android.systemui.ScreenDecorations.rectsToRegion;
+import static com.android.systemui.ScreenDecorations.rectsToRegion;
import static com.android.systemui.tuner.TunablePadding.FLAG_END;
import static com.android.systemui.tuner.TunablePadding.FLAG_START;
import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -87,7 +87,6 @@
mWindowManager = mock(WindowManager.class);
mView = spy(new StatusBarWindowView(mContext, null));
when(mStatusBar.getStatusBarWindow()).thenReturn(mView);
- when(mStatusBar.getNavigationBarWindow()).thenReturn(mView);
mContext.putComponent(StatusBar.class, mStatusBar);
Display display = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
@@ -135,9 +134,12 @@
public void testNoRounding_NoCutout() {
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
- mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 0);
- mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius_top, 0);
- mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius_bottom, 0);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius, 0);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_top, 0);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 0);
@@ -154,7 +156,8 @@
public void testRounding() {
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
- mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 20);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius, 20);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 20);
@@ -174,7 +177,8 @@
public void testCutout() {
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, true);
- mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 0);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius, 0);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 0);
@@ -187,7 +191,8 @@
public void testDelayedCutout() {
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
- mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 0);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius, 0);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 0);
@@ -235,12 +240,14 @@
public void testUpdateRoundedCorners() {
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
- mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 20);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius, 20);
mScreenDecorations.start();
assertEquals(mScreenDecorations.mRoundedDefault, 20);
- mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 5);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius, 5);
mScreenDecorations.onConfigurationChanged(null);
assertEquals(mScreenDecorations.mRoundedDefault, 5);
}
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 a04e57b..51d94f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -16,6 +16,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -42,10 +43,10 @@
@Before
public void setup() {
- mCommandQueue = new CommandQueue();
+ mCommandQueue = new CommandQueue(mContext);
mCallbacks = mock(Callbacks.class);
mCommandQueue.addCallback(mCallbacks);
- verify(mCallbacks).disable(eq(0), eq(0), eq(false));
+ verify(mCallbacks).disable(anyInt(), eq(0), eq(0), eq(false));
}
@After
@@ -73,7 +74,7 @@
int state2 = 42;
mCommandQueue.disable(DEFAULT_DISPLAY, state1, state2);
waitForIdleSync();
- verify(mCallbacks).disable(eq(state1), eq(state2), eq(true));
+ verify(mCallbacks).disable(eq(DEFAULT_DISPLAY), eq(state1), eq(state2), eq(true));
}
@Test
@@ -104,7 +105,8 @@
Rect r = new Rect();
mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r);
waitForIdleSync();
- verify(mCallbacks).setSystemUiVisibility(eq(1), eq(2), eq(3), eq(4), eq(null), eq(r));
+ verify(mCallbacks).setSystemUiVisibility(eq(DEFAULT_DISPLAY), eq(1), eq(2), eq(3), eq(4),
+ eq(null), eq(r));
}
// TODO(b/117478341): add test case for multi-display
@@ -112,7 +114,7 @@
public void testTopAppWindowChanged() {
mCommandQueue.topAppWindowChanged(DEFAULT_DISPLAY, true);
waitForIdleSync();
- verify(mCallbacks).topAppWindowChanged(eq(true));
+ verify(mCallbacks).topAppWindowChanged(eq(DEFAULT_DISPLAY), eq(true));
}
// TODO(b/117478341): add test case for multi-display
@@ -120,7 +122,8 @@
public void testShowImeButton() {
mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true);
waitForIdleSync();
- verify(mCallbacks).setImeWindowStatus(eq(null), eq(1), eq(2), eq(true));
+ verify(mCallbacks).setImeWindowStatus(
+ eq(DEFAULT_DISPLAY), eq(null), eq(1), eq(2), eq(true));
}
@Test
@@ -177,7 +180,7 @@
public void testSetWindowState() {
mCommandQueue.setWindowState(DEFAULT_DISPLAY, 1, 2);
waitForIdleSync();
- verify(mCallbacks).setWindowState(eq(1), eq(2));
+ verify(mCallbacks).setWindowState(eq(DEFAULT_DISPLAY), eq(1), eq(2));
}
@Test
@@ -192,7 +195,7 @@
public void testAppTransitionPending() {
mCommandQueue.appTransitionPending(DEFAULT_DISPLAY);
waitForIdleSync();
- verify(mCallbacks).appTransitionPending(eq(false));
+ verify(mCallbacks).appTransitionPending(eq(DEFAULT_DISPLAY), eq(false));
}
// TODO(b/117478341): add test case for multi-display
@@ -200,7 +203,7 @@
public void testAppTransitionCancelled() {
mCommandQueue.appTransitionCancelled(DEFAULT_DISPLAY);
waitForIdleSync();
- verify(mCallbacks).appTransitionCancelled();
+ verify(mCallbacks).appTransitionCancelled(eq(DEFAULT_DISPLAY));
}
// TODO(b/117478341): add test case for multi-display
@@ -208,7 +211,8 @@
public void testAppTransitionStarting() {
mCommandQueue.appTransitionStarting(DEFAULT_DISPLAY, 1, 2);
waitForIdleSync();
- verify(mCallbacks).appTransitionStarting(eq(1L), eq(2L), eq(false));
+ verify(mCallbacks).appTransitionStarting(
+ eq(DEFAULT_DISPLAY), eq(1L), eq(2L), eq(false));
}
// TODO(b/117478341): add test case for multi-display
@@ -216,7 +220,7 @@
public void testAppTransitionFinished() {
mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY);
waitForIdleSync();
- verify(mCallbacks).appTransitionFinished();
+ verify(mCallbacks).appTransitionFinished(eq(DEFAULT_DISPLAY));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 7b96518..983ca83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -42,6 +42,7 @@
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -51,6 +52,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -72,9 +75,11 @@
// Dependency mocks:
@Mock private NotificationEntryManager mEntryManager;
@Mock private NotificationListener mListener;
+ @Captor private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
private NotificationData.Entry mEntry;
private TestableNotificationLogger mLogger;
+ private NotificationEntryListener mNotificationEntryListener;
private ConcurrentLinkedQueue<AssertionError> mErrorQueue = new ConcurrentLinkedQueue<>();
@Before
@@ -94,6 +99,8 @@
mLogger = new TestableNotificationLogger(mListener, Dependency.get(UiOffloadThread.class),
mEntryManager, mock(StatusBarStateController.class), mBarService);
mLogger.setUpWithContainer(mListContainer);
+ verify(mEntryManager).addNotificationEntryListener(mEntryListenerCaptor.capture());
+ mNotificationEntryListener = mEntryListenerCaptor.getValue();
}
@Test
@@ -152,6 +159,11 @@
verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any());
}
+ @Test
+ public void testHandleNullEntryOnEntryRemoved() {
+ mNotificationEntryListener.onEntryRemoved(null, "foobar", null, null, false, false);
+ }
+
private class TestableNotificationLogger extends NotificationLogger {
TestableNotificationLogger(NotificationListener notificationListener,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index d99e46d..add8838 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -14,6 +14,8 @@
package com.android.systemui.statusbar.phone;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
@@ -74,7 +76,7 @@
CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
fragment.initNotificationIconArea(mMockNotificiationAreaController);
- fragment.disable(0, 0, false);
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
.getVisibility());
@@ -89,12 +91,12 @@
CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
fragment.initNotificationIconArea(mMockNotificiationAreaController);
- fragment.disable(StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
+ fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
.getVisibility());
- fragment.disable(0, 0, false);
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
.getVisibility());
@@ -107,11 +109,11 @@
CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
fragment.initNotificationIconArea(mMockNotificiationAreaController);
- fragment.disable(StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
+ fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
Mockito.verify(mNotificationAreaInner).setVisibility(eq(View.INVISIBLE));
- fragment.disable(0, 0, false);
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
}
@@ -123,11 +125,11 @@
CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
fragment.initNotificationIconArea(mMockNotificiationAreaController);
- fragment.disable(StatusBarManager.DISABLE_CLOCK, 0, false);
+ fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_CLOCK, 0, false);
assertEquals(View.GONE, mFragment.getView().findViewById(R.id.clock).getVisibility());
- fragment.disable(0, 0, false);
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock).getVisibility());
}
@@ -139,7 +141,7 @@
CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
fragment.initNotificationIconArea(mMockNotificiationAreaController);
- fragment.disable(StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
+ fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
Mockito.verify(mNotificationAreaInner).setVisibility(eq(View.INVISIBLE));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 4f6329c..38d9ae7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -30,11 +30,11 @@
import android.widget.TextView;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import org.junit.Assert;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java
index 76f57f0..40d5415 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java
@@ -67,5 +67,4 @@
assertTrue(mTransitions.isLightsOut(BarTransitions.MODE_LIGHTS_OUT));
}
-
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
index 72b0156..4f95bc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
@@ -14,17 +14,14 @@
package com.android.systemui.statusbar.phone;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON;
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE;
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import android.graphics.Rect;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.mock;
+
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -32,15 +29,15 @@
import android.widget.LinearLayout;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.statusbar.StatusIconDisplayable;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.StatusBarWifiView;
+import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 4fabe7f..2bbfd7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -14,6 +14,8 @@
package com.android.systemui.statusbar.phone;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -61,7 +63,7 @@
public void setup() {
mMetricsLogger = new FakeMetricsLogger();
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
- mCommandQueue = new CommandQueue();
+ mCommandQueue = new CommandQueue(mContext);
mContext.putComponent(CommandQueue.class, mCommandQueue);
mDependency.injectTestDependency(ShadeController.class, mShadeController);
@@ -81,7 +83,8 @@
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- mCommandQueue.disable(StatusBarManager.DISABLE_EXPAND, 0, false /* animate */);
+ mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
+ false /* animate */);
TestableLooper.get(this).processAllMessages();
assertFalse("The panel shouldn't allow heads up while disabled",
@@ -94,7 +97,8 @@
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- mCommandQueue.disable(0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */);
+ mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
+ false /* animate */);
TestableLooper.get(this).processAllMessages();
assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index e80694c..d57d565 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -18,6 +18,7 @@
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.view.Display.DEFAULT_DISPLAY;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -82,6 +83,7 @@
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -196,6 +198,7 @@
mDreamManager);
mDependency.injectTestDependency(NotificationInterruptionStateProvider.class,
mNotificationInterruptionStateProvider);
+ mDependency.injectMockDependency(NavigationBarController.class);
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -245,7 +248,8 @@
mock(StatusBarWindowController.class), mock(NotificationIconAreaController.class),
mDozeScrimController, mock(NotificationShelf.class),
mLockscreenUserManager, mCommandQueue, mNotificationPresenter,
- mock(BubbleController.class));
+ mock(BubbleController.class), mock(NavigationBarController.class),
+ mock(AutoHideController.class));
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mContext.getComponents();
SystemUIFactory.getInstance().getRootComponent()
@@ -544,7 +548,7 @@
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
when(mCommandQueue.panelsEnabled()).thenReturn(false);
- mStatusBar.disable(StatusBarManager.DISABLE_NONE,
+ mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
verify(mNotificationPanelView).setQsExpansionEnabled(false);
mStatusBar.animateExpandNotificationsPanel();
@@ -553,7 +557,8 @@
verify(mNotificationPanelView, never()).expand(anyBoolean());
when(mCommandQueue.panelsEnabled()).thenReturn(true);
- mStatusBar.disable(StatusBarManager.DISABLE_NONE, StatusBarManager.DISABLE2_NONE, false);
+ mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
+ StatusBarManager.DISABLE2_NONE, false);
verify(mNotificationPanelView).setQsExpansionEnabled(true);
mStatusBar.animateExpandNotificationsPanel();
verify(mNotificationPanelView).expandWithoutQs();
@@ -695,7 +700,9 @@
NotificationLockscreenUserManager notificationLockscreenUserManager,
CommandQueue commandQueue,
NotificationPresenter notificationPresenter,
- BubbleController bubbleController) {
+ BubbleController bubbleController,
+ NavigationBarController navBarController,
+ AutoHideController autoHideController) {
mStatusBarKeyguardViewManager = man;
mUnlockMethodCache = unlock;
mKeyguardIndicationController = key;
@@ -726,6 +733,8 @@
mPresenter = notificationPresenter;
mGestureWakeLock = mock(PowerManager.WakeLock.class);
mBubbleController = bubbleController;
+ mNavigationBarController = navBarController;
+ mAutoHideController = autoHideController;
}
private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java
index e3a41be..8b8e3f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java
@@ -14,11 +14,11 @@
package com.android.systemui.statusbar.policy;
-import static com.google.common.truth.Truth.assertThat;
-
import static junit.framework.TestCase.assertTrue;
+
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -59,7 +59,7 @@
mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.TRUE);
mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.FALSE);
assertFalse(mRemoteInputQuickSettingsDisabler.mRemoteInputActive);
- verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean());
+ verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
}
@Test
@@ -67,7 +67,7 @@
mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.FALSE);
mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.TRUE);
assertTrue(mRemoteInputQuickSettingsDisabler.mRemoteInputActive);
- verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean());
+ verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
}
@Test
@@ -78,7 +78,7 @@
c.orientation = Configuration.ORIENTATION_LANDSCAPE;
mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
assertTrue(mRemoteInputQuickSettingsDisabler.misLandscape);
- verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean());
+ verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
}
@Test
@@ -89,7 +89,7 @@
c.orientation = Configuration.ORIENTATION_PORTRAIT;
mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
assertFalse(mRemoteInputQuickSettingsDisabler.misLandscape);
- verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean());
+ verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
}
}
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/res/values/config.xml b/packages/overlays/IconShapeRoundedRectOverlay/res/values/config.xml
index f024615..138c283 100644
--- a/packages/overlays/IconShapeRoundedRectOverlay/res/values/config.xml
+++ b/packages/overlays/IconShapeRoundedRectOverlay/res/values/config.xml
@@ -18,7 +18,7 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
- <string name="config_icon_mask" translatable="false">"M50,0L92,0C96.42,0 100,4.58 100 8L100,92C100, 96.42 96.42 100 92 100L8 100C4.58, 100 0 96.42 0 92L0 8 C 0 4.42 4.42 0 8 0L50 0Z"</string>
+ <string name="config_icon_mask" translatable="false">"M50,0L88,0 C94.4,0 100,5.4 100 12 L100,88 C100,94.6 94.6 100 88 100 L12,100 C5.4,100 0,94.6 0,88 L0 12 C0 5.4 5.4 0 12 0 L50,0 Z"</string>
<!-- Flag indicating whether round icons should be parsed from the application manifest. -->
<bool name="config_useRoundIcon">false</bool>
diff --git a/packages/overlays/IconShapeTeardropOverlay/res/values/config.xml b/packages/overlays/IconShapeTeardropOverlay/res/values/config.xml
index 43ad04d..818e696 100644
--- a/packages/overlays/IconShapeTeardropOverlay/res/values/config.xml
+++ b/packages/overlays/IconShapeTeardropOverlay/res/values/config.xml
@@ -18,7 +18,7 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
- <string name="config_icon_mask" translatable="false">"M50,0A50,50,0,0 1 100,50 L100,85 A15,15,0,0 1 85,100 L50,100 A50,50,0,0 1 50,0z"</string>
+ <string name="config_icon_mask" translatable="false">"M50,0 C77.6,0 100,22.4 100,50 L100,88 C100,94.6 94.6,100 88,100 L50,100 C22.4 100 0 77.6 0 50C0 22.4 22.4 0 50 0 Z"</string>
<!-- Flag indicating whether round icons should be parsed from the application manifest. -->
<bool name="config_useRoundIcon">false</bool>
diff --git a/services/accessibility/TEST_MAPPING b/services/accessibility/TEST_MAPPING
index 320924c..d90c3bd 100644
--- a/services/accessibility/TEST_MAPPING
+++ b/services/accessibility/TEST_MAPPING
@@ -7,7 +7,7 @@
"include-annotation": "android.platform.test.annotations.Presubmit"
},
{
- "exclude-annotation": "android.support.test.filters.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
@@ -18,7 +18,7 @@
"include-annotation": "android.platform.test.annotations.Presubmit"
},
{
- "exclude-annotation": "android.support.test.filters.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
@@ -29,7 +29,7 @@
"include-annotation": "android.platform.test.annotations.Presubmit"
},
{
- "exclude-annotation": "android.support.test.filters.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
@@ -40,7 +40,7 @@
"include-filter": "com.android.server.accessibility"
},
{
- "exclude-annotation": "android.support.test.filters.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
@@ -51,7 +51,7 @@
"include-filter": "com.android.internal.accessibility"
},
{
- "exclude-annotation": "android.support.test.filters.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
@@ -62,7 +62,7 @@
"include-filter": "android.view.accessibility"
},
{
- "exclude-annotation": "android.support.test.filters.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 0e9b407..0348f2b 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -64,9 +64,11 @@
import android.os.SystemClock;
import android.service.autofill.AutofillFieldClassificationService.Scores;
import android.service.autofill.AutofillService;
+import android.service.autofill.CompositeUserData;
import android.service.autofill.Dataset;
import android.service.autofill.FieldClassification;
import android.service.autofill.FieldClassification.Match;
+import android.service.autofill.FieldClassificationUserData;
import android.service.autofill.FillContext;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
@@ -1237,11 +1239,16 @@
return;
}
+ // Merge UserData if necessary.
+ // Fields in packageUserData will override corresponding fields in genericUserData.
+ final UserData genericUserData = mService.getUserData();
final UserData packageUserData = lastResponse.getUserData();
-
- final UserData userData;
- if (packageUserData != null) {
- // Replace default userData
+ final FieldClassificationUserData userData;
+ if (packageUserData == null && genericUserData == null) {
+ userData = null;
+ } else if (packageUserData != null && genericUserData != null) {
+ userData = new CompositeUserData(genericUserData, packageUserData);
+ } else if (packageUserData != null) {
userData = packageUserData;
} else {
userData = mService.getUserData();
@@ -1396,7 +1403,8 @@
@NonNull ArrayList<String> changedDatasetIds,
@NonNull ArrayList<AutofillId> manuallyFilledFieldIds,
@NonNull ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
- @NonNull UserData userData, @NonNull Collection<ViewState> viewStates) {
+ @NonNull FieldClassificationUserData userData,
+ @NonNull Collection<ViewState> viewStates) {
final String[] userValues = userData.getValues();
final String[] categoryIds = userData.getCategoryIds();
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 7049744..8b07db7 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -36,7 +36,6 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Binder;
-import android.os.Environment;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -50,11 +49,7 @@
import com.android.server.SystemConfig;
import com.android.server.SystemService;
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Set;
@@ -72,10 +67,6 @@
public static final boolean MORE_DEBUG = false;
public static final boolean DEBUG_SCHEDULING = true;
- // File containing backup-enabled state. Contains a single byte to denote enabled status.
- // Nonzero is enabled; file missing or a zero byte is disabled.
- private static final String BACKUP_ENABLE_FILE = "backup_enabled";
-
// The published binder is a singleton Trampoline object that calls through to the proper code.
// This indirection lets us turn down the heavy implementation object on the fly without
// disturbing binders that have been cached elsewhere in the system.
@@ -150,7 +141,8 @@
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
try {
// TODO(b/121198604): Make enable file per-user and clean up indirection.
- mTrampoline.setBackupEnabledForUser(userId, readBackupEnableState(userId));
+ mTrampoline.setBackupEnabledForUser(
+ userId, UserBackupManagerFilePersistedSettings.readBackupEnableState(userId));
} catch (RemoteException e) {
// Can't happen, it's a local object.
}
@@ -773,44 +765,6 @@
}
}
- private static boolean readBackupEnableState(int userId) {
- File base = new File(Environment.getDataDirectory(), "backup");
- File enableFile = new File(base, BACKUP_ENABLE_FILE);
- if (enableFile.exists()) {
- try (FileInputStream fin = new FileInputStream(enableFile)) {
- int state = fin.read();
- return state != 0;
- } catch (IOException e) {
- // can't read the file; fall through to assume disabled
- Slog.e(TAG, "Cannot read enable state; assuming disabled");
- }
- } else {
- if (DEBUG) {
- Slog.i(TAG, "isBackupEnabled() => false due to absent settings file");
- }
- }
- return false;
- }
-
- static void writeBackupEnableState(boolean enable, int userId) {
- File base = new File(Environment.getDataDirectory(), "backup");
- File enableFile = new File(base, BACKUP_ENABLE_FILE);
- File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
- try (FileOutputStream fout = new FileOutputStream(stage)) {
- fout.write(enable ? 1 : 0);
- fout.close();
- stage.renameTo(enableFile);
- // will be synced immediately by the try-with-resources call to close()
- } catch (IOException | RuntimeException e) {
- Slog.e(
- TAG,
- "Unable to record backup enable state; reverting to disabled: "
- + e.getMessage());
- enableFile.delete();
- stage.delete();
- }
- }
-
/** Implementation to receive lifecycle event callbacks for system services. */
public static final class Lifecycle extends SystemService {
public Lifecycle(Context context) {
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerFilePersistedSettings.java b/services/backup/java/com/android/server/backup/UserBackupManagerFilePersistedSettings.java
new file mode 100644
index 0000000..6a1de63
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerFilePersistedSettings.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import static com.android.server.backup.BackupManagerService.DEBUG;
+import static com.android.server.backup.BackupManagerService.TAG;
+
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/** User settings which are persisted across reboots. */
+final class UserBackupManagerFilePersistedSettings {
+ // File containing backup-enabled state. Contains a single byte to denote enabled status.
+ // Nonzero is enabled; file missing or a zero byte is disabled.
+ private static final String BACKUP_ENABLE_FILE = "backup_enabled";
+
+ static boolean readBackupEnableState(int userId) {
+ return readBackupEnableState(UserBackupManagerFiles.getBaseStateDir(userId));
+ }
+
+ static void writeBackupEnableState(int userId, boolean enable) {
+ writeBackupEnableState(UserBackupManagerFiles.getBaseStateDir(userId), enable);
+ }
+
+ private static boolean readBackupEnableState(File baseDir) {
+ File enableFile = new File(baseDir, BACKUP_ENABLE_FILE);
+ if (enableFile.exists()) {
+ try (FileInputStream fin = new FileInputStream(enableFile)) {
+ int state = fin.read();
+ return state != 0;
+ } catch (IOException e) {
+ // can't read the file; fall through to assume disabled
+ Slog.e(TAG, "Cannot read enable state; assuming disabled");
+ }
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "isBackupEnabled() => false due to absent settings file");
+ }
+ }
+ return false;
+ }
+
+ private static void writeBackupEnableState(File baseDir, boolean enable) {
+ File enableFile = new File(baseDir, BACKUP_ENABLE_FILE);
+ File stage = new File(baseDir, BACKUP_ENABLE_FILE + "-stage");
+ try (FileOutputStream fout = new FileOutputStream(stage)) {
+ fout.write(enable ? 1 : 0);
+ fout.close();
+ stage.renameTo(enableFile);
+ // will be synced immediately by the try-with-resources call to close()
+ } catch (IOException | RuntimeException e) {
+ Slog.e(
+ TAG,
+ "Unable to record backup enable state; reverting to disabled: "
+ + e.getMessage());
+ enableFile.delete();
+ stage.delete();
+ }
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
new file mode 100644
index 0000000..a0feaf9
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import android.os.Environment;
+
+import java.io.File;
+
+/** Directories used for user specific backup/restore persistent state and book-keeping. */
+public final class UserBackupManagerFiles {
+ // Name of the directories the service stores bookkeeping data under.
+ private static final String BACKUP_PERSISTENT_DIR = "backup";
+ private static final String BACKUP_STAGING_DIR = "backup_stage";
+
+ static File getBaseStateDir(int userId) {
+ // TODO (b/120424138) this should be per user
+ return new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR);
+ }
+
+ static File getDataDir(int userId) {
+ // TODO (b/120424138) this should be per user
+ // This dir on /cache is managed directly in init.rc
+ return new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
+ }
+
+ /** Directory used by full backup engine to store state. */
+ public static File getFullBackupEngineFilesDir(int userId) {
+ // TODO (b/120424138) this should be per user
+ return new File("/data/system");
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 2e41443..6425508 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -70,7 +70,6 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
-import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -164,17 +163,9 @@
/** System service that performs backup/restore operations. */
public class UserBackupManagerService {
- // File containing backup-enabled state. Contains a single byte;
- // nonzero == enabled. File missing or contains a zero byte == disabled.
- private static final String BACKUP_ENABLE_FILE = "backup_enabled";
-
// Persistently track the need to do a full init.
private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
- // Name of the directories the service stores bookkeeping data under.
- private static final String BACKUP_PERSISTENT_DIR = "backup";
- private static final String BACKUP_STAGING_DIR = "backup_stage";
-
// System-private key used for backing up an app's widget state. Must
// begin with U+FFxx by convention (we reserve all keys starting
// with U+FF00 or higher for system use).
@@ -354,9 +345,9 @@
final AtomicInteger mNextToken = new AtomicInteger();
// Where we keep our journal files and other bookkeeping.
- private File mBaseStateDir;
- private File mDataDir;
- private File mJournalDir;
+ private final File mBaseStateDir;
+ private final File mDataDir;
+ private final File mJournalDir;
@Nullable
private DataChangedJournal mJournal;
private File mFullBackupScheduleFile;
@@ -395,10 +386,8 @@
TransportManager transportManager =
new TransportManager(context, transportWhitelist, currentTransport);
- File baseStateDir = new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR);
-
- // This dir on /cache is managed directly in init.rc
- File dataDir = new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
+ File baseStateDir = UserBackupManagerFiles.getBaseStateDir(userId);
+ File dataDir = UserBackupManagerFiles.getDataDir(userId);
return createAndInitializeService(
userId, context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
@@ -726,18 +715,10 @@
return mBaseStateDir;
}
- public void setBaseStateDir(File baseStateDir) {
- mBaseStateDir = baseStateDir;
- }
-
public File getDataDir() {
return mDataDir;
}
- public void setDataDir(File dataDir) {
- mDataDir = dataDir;
- }
-
@Nullable
public DataChangedJournal getJournal() {
return mJournal;
@@ -2735,8 +2716,7 @@
try {
boolean wasEnabled = mEnabled;
synchronized (this) {
- // TODO(b/118520567): Clean up writing backup enabled logic.
- BackupManagerService.writeBackupEnableState(enable, UserHandle.USER_SYSTEM);
+ UserBackupManagerFilePersistedSettings.writeBackupEnableState(mUserId, enable);
mEnabled = enable;
}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index 5e92339..45ca2af 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -24,6 +24,7 @@
import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
+import android.annotation.UserIdInt;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
import android.app.backup.BackupTransport;
@@ -40,6 +41,7 @@
import com.android.server.AppWidgetBackupBridge;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.UserBackupManagerFiles;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.remote.RemoteCall;
import com.android.server.backup.utils.FullBackupUtils;
@@ -66,6 +68,7 @@
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
class FullBackupRunner implements Runnable {
+ private final @UserIdInt int mUserId;
private final PackageManager mPackageManager;
private final PackageInfo mPackage;
private final IBackupAgent mAgent;
@@ -81,13 +84,15 @@
int token,
boolean includeApks)
throws IOException {
+ // TODO: http://b/22388012
+ mUserId = UserHandle.USER_SYSTEM;
mPackageManager = backupManagerService.getPackageManager();
mPackage = packageInfo;
mAgent = agent;
mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
mToken = token;
mIncludeApks = includeApks;
- mFilesDir = new File("/data/system");
+ mFilesDir = UserBackupManagerFiles.getFullBackupEngineFilesDir(mUserId);
}
@Override
@@ -114,10 +119,8 @@
manifestFile.delete();
// Write widget data.
- // TODO: http://b/22388012
byte[] widgetData =
- AppWidgetBackupBridge.getWidgetState(
- packageName, UserHandle.USER_SYSTEM);
+ AppWidgetBackupBridge.getWidgetState(packageName, mUserId);
if (widgetData != null && widgetData.length > 0) {
File metadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
appMetadataBackupWriter.backupWidget(
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
new file mode 100644
index 0000000..06dc918
--- /dev/null
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Monitors the health of packages on the system and notifies interested observers when packages
+ * fail. All registered observers will be notified until an observer takes a mitigation action.
+ */
+public class PackageWatchdog {
+ private static final String TAG = "PackageWatchdog";
+ // Duration to count package failures before it resets to 0
+ private static final int TRIGGER_DURATION_MS = 60000;
+ // Number of package failures within the duration above before we notify observers
+ private static final int TRIGGER_FAILURE_COUNT = 5;
+ private static final int DB_VERSION = 1;
+ private static final String TAG_PACKAGE_WATCHDOG = "package-watchdog";
+ private static final String TAG_PACKAGE = "package";
+ private static final String TAG_OBSERVER = "observer";
+ private static final String ATTR_VERSION = "version";
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_DURATION = "duration";
+ private static final int MESSAGE_SAVE_FILE = 1;
+
+ private static PackageWatchdog sPackageWatchdog;
+
+ private final Object mLock = new Object();
+ // System server context
+ private final Context mContext;
+ // Handler to run package cleanup runnables
+ private final Handler mTimerHandler;
+ private final HandlerThread mIoThread = new HandlerThread("package_watchdog_io",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ private final Handler mIoHandler;
+ // Maps observer names to package observers that have been registered since the last boot
+ @GuardedBy("mLock")
+ final Map<String, PackageHealthObserver> mRegisteredObservers = new ArrayMap<>();
+ // Maps observer names to internal observers (registered or not) loaded from file
+ @GuardedBy("mLock")
+ final Map<String, ObserverInternal> mAllObservers = new ArrayMap<>();
+ // /data/system/ directory
+ private final File mSystemDir = new File(Environment.getDataDirectory(), "system");
+ // File containing the XML data of monitored packages
+ private final AtomicFile mPolicyFile =
+ new AtomicFile(new File(mSystemDir, "package-watchdog.xml"));
+ // Runnable to prune monitored packages that have expired
+ private final Runnable mPackageCleanup;
+ // Last SystemClock#uptimeMillis a package clean up was executed.
+ // 0 if mPackageCleanup not running.
+ private long mUptimeAtLastRescheduleMs;
+ // Duration a package cleanup was last scheduled for.
+ // 0 if mPackageCleanup not running.
+ private long mDurationAtLastReschedule;
+
+ private PackageWatchdog(Context context) {
+ mContext = context;
+ mTimerHandler = new Handler(Looper.myLooper());
+ mIoThread.start();
+ mIoHandler = new IoHandler(mIoThread.getLooper());
+ mPackageCleanup = this::rescheduleCleanup;
+ loadFromFile();
+ }
+
+ /** Creates or gets singleton instance of PackageWatchdog. */
+ public static synchronized PackageWatchdog getInstance(Context context) {
+ if (sPackageWatchdog == null) {
+ sPackageWatchdog = new PackageWatchdog(context);
+ }
+ return sPackageWatchdog;
+ }
+
+ /**
+ * Registers {@code observer} to listen for package failures
+ *
+ * <p>Observers are expected to call this on boot. It does not specify any packages but
+ * it will resume observing any packages requested from a previous boot.
+ */
+ public void registerHealthObserver(PackageHealthObserver observer) {
+ synchronized (mLock) {
+ mRegisteredObservers.put(observer.getName(), observer);
+ if (mDurationAtLastReschedule == 0) {
+ // Nothing running, schedule
+ rescheduleCleanup();
+ }
+ }
+ }
+
+ /**
+ * Starts observing the health of the {@code packages} for {@code observer} and notifies
+ * {@code observer} of any package failures within the monitoring duration.
+ *
+ * <p>If {@code observer} is already monitoring a package in {@code packageNames},
+ * the monitoring window of that package will be reset to {@code hours}.
+ *
+ * @throws IllegalArgumentException if {@code packageNames} is empty
+ * or {@code hours} is less than 1
+ */
+ public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames,
+ int hours) {
+ if (packageNames.isEmpty() || hours < 1) {
+ throw new IllegalArgumentException("Observation not started, no packages specified"
+ + "or invalid hours");
+ }
+ long durationMs = TimeUnit.HOURS.toMillis(hours);
+ List<MonitoredPackage> packages = new ArrayList<>();
+ for (String packageName : packageNames) {
+ packages.add(new MonitoredPackage(packageName, durationMs));
+ }
+ synchronized (mLock) {
+ ObserverInternal oldObserver = mAllObservers.get(observer.getName());
+ if (oldObserver == null) {
+ Slog.d(TAG, observer.getName() + " started monitoring health of packages "
+ + packageNames);
+ mAllObservers.put(observer.getName(),
+ new ObserverInternal(observer.getName(), packages));
+ } else {
+ Slog.d(TAG, observer.getName() + " added the following packages to monitor "
+ + packageNames);
+ oldObserver.updatePackages(packages);
+ }
+ }
+ registerHealthObserver(observer);
+ // Always reschedule because we may need to expire packages
+ // earlier than we are already scheduled for
+ rescheduleCleanup();
+ sendIoMessage(MESSAGE_SAVE_FILE);
+ }
+
+ /**
+ * Unregisters {@code observer} from listening to package failure.
+ * Additionally, this stops observing any packages that may have previously been observed
+ * even from a previous boot.
+ */
+ public void unregisterHealthObserver(PackageHealthObserver observer) {
+ synchronized (mLock) {
+ mAllObservers.remove(observer.getName());
+ mRegisteredObservers.remove(observer.getName());
+ }
+ sendIoMessage(MESSAGE_SAVE_FILE);
+ }
+
+ // TODO(zezeozue:) Accept current versionCodes of failing packages?
+ /**
+ * Called when a process fails either due to a crash or ANR.
+ *
+ * <p>All registered observers for the packages contained in the process will be notified in
+ * order of priority unitl an observer signifies that it has taken action and other observers
+ * should not notified.
+ *
+ * <p>This method could be called frequently if there is a severe problem on the device.
+ */
+ public void onPackageFailure(String[] packages) {
+ synchronized (mLock) {
+ if (mRegisteredObservers.isEmpty()) {
+ return;
+ }
+ for (String packageName : packages) {
+ for (ObserverInternal observer : mAllObservers.values()) {
+ if (observer.onPackageFailure(packageName)) {
+ PackageHealthObserver activeObserver =
+ mRegisteredObservers.get(observer.mName);
+ if (activeObserver != null
+ && activeObserver.onHealthCheckFailed(packageName)) {
+ // Observer has handled, do not notify other observers
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // TODO(zezeozue): Optimize write? Maybe only write a separate smaller file?
+ // This currently adds about 7ms extra to shutdown thread
+ /** Writes the package information to file during shutdown. */
+ public void writeNow() {
+ if (!mAllObservers.isEmpty()) {
+ mIoHandler.removeMessages(MESSAGE_SAVE_FILE);
+ pruneObservers(SystemClock.uptimeMillis() - mUptimeAtLastRescheduleMs);
+ saveToFile();
+ Slog.i(TAG, "Last write to update package durations");
+ }
+ }
+
+ /** Register instances of this interface to receive notifications on package failure. */
+ public interface PackageHealthObserver {
+ /**
+ * Called when health check fails for the {@code packages}.
+ * @return {@code true} if action was taken and other observers should not be notified of
+ * this failure, {@code false} otherwise.
+ */
+ boolean onHealthCheckFailed(String packageName);
+
+ // TODO(zezeozue): Ensure uniqueness?
+ /**
+ * Identifier for the observer, should not change across device updates otherwise the
+ * watchdog may drop observing packages with the old name.
+ */
+ String getName();
+ }
+
+ /** Reschedules handler to prune expired packages from observers. */
+ private void rescheduleCleanup() {
+ synchronized (mLock) {
+ long nextDurationToScheduleMs = getEarliestPackageExpiryLocked();
+ if (nextDurationToScheduleMs == Long.MAX_VALUE) {
+ Slog.i(TAG, "No monitored packages, ending package cleanup");
+ mDurationAtLastReschedule = 0;
+ mUptimeAtLastRescheduleMs = 0;
+ return;
+ }
+ long uptimeMs = SystemClock.uptimeMillis();
+ // O if mPackageCleanup not running
+ long elapsedDurationMs = mUptimeAtLastRescheduleMs == 0
+ ? 0 : uptimeMs - mUptimeAtLastRescheduleMs;
+ // O if mPackageCleanup not running
+ long remainingDurationMs = mDurationAtLastReschedule - elapsedDurationMs;
+
+ if (mUptimeAtLastRescheduleMs == 0 || nextDurationToScheduleMs < remainingDurationMs) {
+ // First schedule or an earlier reschedule
+ pruneObservers(elapsedDurationMs);
+ mTimerHandler.removeCallbacks(mPackageCleanup);
+ mTimerHandler.postDelayed(mPackageCleanup, nextDurationToScheduleMs);
+ mDurationAtLastReschedule = nextDurationToScheduleMs;
+ mUptimeAtLastRescheduleMs = uptimeMs;
+ }
+ }
+ }
+
+ /**
+ * Returns the earliest time a package should expire.
+ * @returns Long#MAX_VALUE if there are no observed packages.
+ */
+ private long getEarliestPackageExpiryLocked() {
+ long shortestDurationMs = Long.MAX_VALUE;
+ for (ObserverInternal observer : mAllObservers.values()) {
+ for (MonitoredPackage p : observer.mPackages.values()) {
+ if (p.mDurationMs < shortestDurationMs) {
+ shortestDurationMs = p.mDurationMs;
+ }
+ }
+ }
+ Slog.v(TAG, "Earliest package time is " + shortestDurationMs);
+ return shortestDurationMs;
+ }
+
+ /**
+ * Removes {@code elapsedMs} milliseconds from all durations on monitored packages.
+ * Discards expired packages and discards observers without any packages.
+ */
+ private void pruneObservers(long elapsedMs) {
+ if (elapsedMs == 0) {
+ return;
+ }
+ synchronized (mLock) {
+ Slog.d(TAG, "Removing expired packages after " + elapsedMs + "ms");
+ Iterator<ObserverInternal> it = mAllObservers.values().iterator();
+ while (it.hasNext()) {
+ ObserverInternal observer = it.next();
+ if (!observer.updateMonitoringDurations(elapsedMs)) {
+ Slog.i(TAG, "Discarding observer " + observer.mName + ". All packages expired");
+ it.remove();
+ }
+ }
+ }
+ sendIoMessage(MESSAGE_SAVE_FILE);
+ }
+
+ /**
+ * Loads mAllObservers from file.
+ *
+ * <p>Note that this is <b>not</b> thread safe and should only called be called
+ * from the constructor.
+ */
+ private void loadFromFile() {
+ InputStream infile = null;
+ mAllObservers.clear();
+ try {
+ infile = mPolicyFile.openRead();
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(infile, StandardCharsets.UTF_8.name());
+ XmlUtils.beginDocument(parser, TAG_PACKAGE_WATCHDOG);
+ int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ ObserverInternal observer = ObserverInternal.read(parser);
+ if (observer != null) {
+ mAllObservers.put(observer.mName, observer);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // Nothing to monitor
+ } catch (IOException e) {
+ Log.wtf(TAG, "Unable to read monitored packages", e);
+ } catch (NumberFormatException e) {
+ Log.wtf(TAG, "Unable to parse monitored package windows", e);
+ } catch (XmlPullParserException e) {
+ Log.wtf(TAG, "Unable to parse monitored packages", e);
+ } finally {
+ IoUtils.closeQuietly(infile);
+ }
+ }
+
+ /**
+ * Persists mAllObservers to file and ignores threshold information.
+ *
+ * <p>Note that this is <b>not</b> thread safe and should only be called on the
+ * single threaded IoHandler.
+ */
+ private boolean saveToFile() {
+ FileOutputStream stream;
+ try {
+ stream = mPolicyFile.startWrite();
+ } catch (IOException e) {
+ Slog.w(TAG, "Cannot update monitored packages", e);
+ return false;
+ }
+
+ try {
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, StandardCharsets.UTF_8.name());
+ out.startDocument(null, true);
+ out.startTag(null, TAG_PACKAGE_WATCHDOG);
+ out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
+ for (ObserverInternal observer : mAllObservers.values()) {
+ observer.write(out);
+ }
+ out.endTag(null, TAG_PACKAGE_WATCHDOG);
+ out.endDocument();
+ mPolicyFile.finishWrite(stream);
+ return true;
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to save monitored packages, restoring backup", e);
+ mPolicyFile.failWrite(stream);
+ return false;
+ } finally {
+ IoUtils.closeQuietly(stream);
+ }
+ }
+
+ private void sendIoMessage(int what) {
+ if (!mIoHandler.hasMessages(what)) {
+ Message m = Message.obtain(mIoHandler, what);
+ mIoHandler.sendMessage(m);
+ }
+ }
+
+ /**
+ * Represents an observer monitoring a set of packages along with the failure thresholds for
+ * each package.
+ */
+ static class ObserverInternal {
+ public final String mName;
+ public final ArrayMap<String, MonitoredPackage> mPackages;
+
+ ObserverInternal(String name, List<MonitoredPackage> packages) {
+ mName = name;
+ mPackages = new ArrayMap<>();
+ updatePackages(packages);
+ }
+
+ /**
+ * Writes important details to file. Doesn't persist any package failure thresholds.
+ *
+ * <p>Note that this method is <b>not</b> thread safe. It should only be called from
+ * #saveToFile which runs on a single threaded handler.
+ */
+ public boolean write(XmlSerializer out) {
+ try {
+ out.startTag(null, TAG_OBSERVER);
+ out.attribute(null, ATTR_NAME, mName);
+ for (int i = 0; i < mPackages.size(); i++) {
+ MonitoredPackage p = mPackages.valueAt(i);
+ out.startTag(null, TAG_PACKAGE);
+ out.attribute(null, ATTR_NAME, p.mName);
+ out.attribute(null, ATTR_DURATION, String.valueOf(p.mDurationMs));
+ out.endTag(null, TAG_PACKAGE);
+ }
+ out.endTag(null, TAG_OBSERVER);
+ return true;
+ } catch (IOException e) {
+ Slog.w(TAG, "Cannot save observer", e);
+ return false;
+ }
+ }
+
+ public void updatePackages(List<MonitoredPackage> packages) {
+ synchronized (mName) {
+ for (MonitoredPackage p : packages) {
+ mPackages.put(p.mName, p);
+ }
+ }
+ }
+
+ /**
+ * Reduces the monitoring durations of all packages observed by this observer by
+ * {@code elapsedMs}. If any duration is less than 0, the package is removed from
+ * observation.
+ *
+ * @returns {@code true} if there are still packages to be observed, {@code false} otherwise
+ */
+ public boolean updateMonitoringDurations(long elapsedMs) {
+ List<MonitoredPackage> packages = new ArrayList<>();
+ synchronized (mName) {
+ Iterator<MonitoredPackage> it = mPackages.values().iterator();
+ while (it.hasNext()) {
+ MonitoredPackage p = it.next();
+ long newDuration = p.mDurationMs - elapsedMs;
+ if (newDuration > 0) {
+ p.mDurationMs = newDuration;
+ } else {
+ it.remove();
+ }
+ }
+ return !mPackages.isEmpty();
+ }
+ }
+
+ /**
+ * Increments failure counts of {@code packageName}.
+ * @returns {@code true} if failure threshold is exceeded, {@code false} otherwise
+ */
+ public boolean onPackageFailure(String packageName) {
+ synchronized (mName) {
+ MonitoredPackage p = mPackages.get(packageName);
+ if (p != null) {
+ return p.onFailure();
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Returns one ObserverInternal from the {@code parser} and advances its state.
+ *
+ * <p>Note that this method is <b>not</b> thread safe. It should only be called from
+ * #loadFromFile which in turn is only called on construction of the
+ * singleton PackageWatchdog.
+ **/
+ public static ObserverInternal read(XmlPullParser parser) {
+ String observerName = null;
+ if (TAG_OBSERVER.equals(parser.getName())) {
+ observerName = parser.getAttributeValue(null, ATTR_NAME);
+ if (TextUtils.isEmpty(observerName)) {
+ return null;
+ }
+ }
+ List<MonitoredPackage> packages = new ArrayList<>();
+ int innerDepth = parser.getDepth();
+ try {
+ while (XmlUtils.nextElementWithin(parser, innerDepth)) {
+ if (TAG_PACKAGE.equals(parser.getName())) {
+ String packageName = parser.getAttributeValue(null, ATTR_NAME);
+ long duration = Long.parseLong(
+ parser.getAttributeValue(null, ATTR_DURATION));
+ if (!TextUtils.isEmpty(packageName)) {
+ packages.add(new MonitoredPackage(packageName, duration));
+ }
+ }
+ }
+ } catch (IOException e) {
+ return null;
+ } catch (XmlPullParserException e) {
+ return null;
+ }
+ if (packages.isEmpty()) {
+ return null;
+ }
+ return new ObserverInternal(observerName, packages);
+ }
+ }
+
+ /** Represents a package along with the time it should be monitored for. */
+ static class MonitoredPackage {
+ public final String mName;
+ // System uptime duration to monitor package
+ public long mDurationMs;
+ // System uptime of first package failure
+ private long mUptimeStartMs;
+ // Number of failures since mUptimeStartMs
+ private int mFailures;
+
+ MonitoredPackage(String name, long durationMs) {
+ mName = name;
+ mDurationMs = durationMs;
+ }
+
+ /**
+ * Increment package failures or resets failure count depending on the last package failure.
+ *
+ * @return {@code true} if failure count exceeds a threshold, {@code false} otherwise
+ */
+ public synchronized boolean onFailure() {
+ final long now = SystemClock.uptimeMillis();
+ final long duration = now - mUptimeStartMs;
+ if (duration > TRIGGER_DURATION_MS) {
+ // TODO(zezeozue): Reseting to 1 is not correct
+ // because there may be more than 1 failure in the last trigger window from now
+ // This is the RescueParty impl, will leave for now
+ mFailures = 1;
+ mUptimeStartMs = now;
+ } else {
+ mFailures++;
+ }
+ return mFailures >= TRIGGER_FAILURE_COUNT;
+ }
+ }
+
+ private class IoHandler extends Handler {
+ IoHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_SAVE_FILE:
+ saveToFile();
+ break;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1a5dd90..16c236e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -336,6 +336,7 @@
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.NetworkManagementInternal;
+import com.android.server.PackageWatchdog;
import com.android.server.RescueParty;
import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
@@ -587,6 +588,7 @@
public final PendingIntentController mPendingIntentController;
final AppErrors mAppErrors;
+ final PackageWatchdog mPackageWatchdog;
/**
* Indicates the maximum time spent waiting for the network rules to get updated.
@@ -2209,6 +2211,7 @@
mContext = mInjector.getContext();
mUiContext = null;
mAppErrors = null;
+ mPackageWatchdog = null;
mActiveUids = new ActiveUids(this, false /* postChangesToAtm */);
mAppOpsService = mInjector.getAppOpsService(null /* file */, null /* handler */);
mBatteryStatsService = null;
@@ -2275,7 +2278,8 @@
mServices = new ActiveServices(this);
mProviderMap = new ProviderMap(this);
- mAppErrors = new AppErrors(mUiContext, this);
+ mPackageWatchdog = PackageWatchdog.getInstance(mUiContext);
+ mAppErrors = new AppErrors(mUiContext, this, mPackageWatchdog);
mActiveUids = new ActiveUids(this, true /* postChangesToAtm */);
final File systemDir = SystemServiceManager.ensureSystemDir();
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 1c1daff..a634b57 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -53,6 +53,7 @@
import com.android.internal.app.ProcessMap;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.server.PackageWatchdog;
import com.android.server.RescueParty;
import com.android.server.wm.WindowProcessController;
@@ -69,6 +70,7 @@
private final ActivityManagerService mService;
private final Context mContext;
+ private final PackageWatchdog mPackageWatchdog;
private ArraySet<String> mAppsNotReportingCrashes;
@@ -93,10 +95,11 @@
private final ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<>();
- AppErrors(Context context, ActivityManagerService service) {
+ AppErrors(Context context, ActivityManagerService service, PackageWatchdog watchdog) {
context.assertRuntimeOverlayThemable();
mService = service;
mContext = context;
+ mPackageWatchdog = watchdog;
}
void writeToProto(ProtoOutputStream proto, long fieldId, String dumpPackage) {
@@ -400,10 +403,16 @@
longMsg = shortMsg;
}
- // If a persistent app is stuck in a crash loop, the device isn't very
- // usable, so we want to consider sending out a rescue party.
- if (r != null && r.isPersistent()) {
- RescueParty.notePersistentAppCrash(mContext, r.uid);
+ if (r != null) {
+ if (r.isPersistent()) {
+ // If a persistent app is stuck in a crash loop, the device isn't very
+ // usable, so we want to consider sending out a rescue party.
+ RescueParty.notePersistentAppCrash(mContext, r.uid);
+ } else {
+ // If a non-persistent app is stuck in crash loop, we want to inform
+ // the package watchdog, maybe an update or experiment can be rolled back.
+ mPackageWatchdog.onPackageFailure(r.getPackageList());
+ }
}
final int relaunchReason = r != null
@@ -821,6 +830,7 @@
void handleShowAnrUi(Message msg) {
Dialog dialogToShow = null;
+ String[] packageList = null;
synchronized (mService) {
AppNotRespondingDialog.Data data = (AppNotRespondingDialog.Data) msg.obj;
final ProcessRecord proc = data.proc;
@@ -828,6 +838,9 @@
Slog.e(TAG, "handleShowAnrUi: proc is null");
return;
}
+ if (!proc.isPersistent()) {
+ packageList = proc.getPackageList();
+ }
if (proc.anrDialog != null) {
Slog.e(TAG, "App already has anr dialog: " + proc);
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
@@ -851,6 +864,10 @@
if (dialogToShow != null) {
dialogToShow.show();
}
+ // Notify PackageWatchdog without the lock held
+ if (packageList != null) {
+ mPackageWatchdog.onPackageFailure(packageList);
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 65cd329..f9a77af 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -67,8 +67,8 @@
sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS_GLES, String.class);
sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYER_APP, String.class);
sGlobalSettingToTypeMap.put(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, int.class);
- sGlobalSettingToTypeMap.put(Settings.Global.UPDATED_GFX_DRIVER_DEV_OPT_IN_APP,
- String.class);
+ sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_IN_APPS, String.class);
+ sGlobalSettingToTypeMap.put(Settings.Global.GUP_BLACK_LIST, String.class);
// add other global settings here...
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 117984e..d133dea 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1362,8 +1362,9 @@
mService.mNativeDebuggingApp = null;
}
- if (app.info.isPrivilegedApp() &&
- DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet())) {
+ if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_PREFER_CODE_INTEGRITY) != 0
+ || (app.info.isPrivilegedApp()
+ && DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet()))) {
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
}
@@ -1613,6 +1614,13 @@
app.addPackage(info.packageName, info.versionCode, mService.mProcessStats);
checkSlow(startTime, "startProcess: done, added package to proc");
return app;
+ } else if (app.getActiveInstrumentation() != null) {
+ // We don't want to kill running instrumentation.
+ if (DEBUG_PROCESSES) {
+ Slog.v(TAG_PROCESSES, "Instrumentation already running: " + app);
+ }
+ checkSlow(startTime, "startProcess: keep instrumentation proc");
+ return app;
}
// An application record is attached to a previous process,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 6cde4ad..d704a3e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -7822,6 +7822,36 @@
return AudioManager.SUCCESS;
}
+ /** see AudioPolicy.setUidDeviceAffinity() */
+ public int setUidDeviceAffinity(IAudioPolicyCallback pcb, int uid,
+ @NonNull int[] deviceTypes,
+ @NonNull String[] deviceAddresses) {
+ synchronized (mAudioPolicies) {
+ final AudioPolicyProxy app =
+ checkUpdateForPolicy(pcb, "Cannot change device affinity in audio policy");
+ if (app == null) {
+ return AudioManager.ERROR;
+ }
+ if (!app.hasMixRoutedToDevices(deviceTypes, deviceAddresses)) {
+ return AudioManager.ERROR;
+ }
+ }
+ return AudioManager.SUCCESS;
+ }
+
+ /** see AudioPolicy.removeUidDeviceAffinity() */
+ public int removeUidDeviceAffinity(IAudioPolicyCallback pcb, int uid) {
+ synchronized (mAudioPolicies) {
+ final AudioPolicyProxy app =
+ checkUpdateForPolicy(pcb, "Cannot remove device affinity in audio policy");
+ if (app == null) {
+ return AudioManager.ERROR;
+ }
+
+ }
+ return AudioManager.SUCCESS;
+ }
+
public int setFocusPropertiesForPolicy(int duckingBehavior, IAudioPolicyCallback pcb) {
if (DEBUG_AP) Log.d(TAG, "setFocusPropertiesForPolicy() duck behavior=" + duckingBehavior
+ " policy " + pcb.asBinder());
@@ -7994,6 +8024,15 @@
//======================
// Audio policy proxy
//======================
+ private static final class AudioDeviceArray {
+ final @NonNull int[] mDeviceTypes;
+ final @NonNull String[] mDeviceAddresses;
+ AudioDeviceArray(@NonNull int[] types, @NonNull String[] addresses) {
+ mDeviceTypes = types;
+ mDeviceAddresses = addresses;
+ }
+ }
+
/**
* This internal class inherits from AudioPolicyConfig, each instance contains all the
* mixes of an AudioPolicy and their configurations.
@@ -8003,6 +8042,8 @@
final IAudioPolicyCallback mPolicyCallback;
final boolean mHasFocusListener;
final boolean mIsVolumeController;
+ final HashMap<Integer, AudioDeviceArray> mUidDeviceAffinities =
+ new HashMap<Integer, AudioDeviceArray>();
/**
* Audio focus ducking behavior for an audio policy.
* This variable reflects the value that was successfully set in
@@ -8075,6 +8116,26 @@
return false;
}
+ // Verify all the devices in the array are served by mixes defined in this policy
+ boolean hasMixRoutedToDevices(@NonNull int[] deviceTypes,
+ @NonNull String[] deviceAddresses) {
+ for (int i = 0; i < deviceTypes.length; i++) {
+ boolean hasDevice = false;
+ for (AudioMix mix : mMixes) {
+ // this will check both that the mix has ROUTE_FLAG_RENDER and the device
+ // is reached by this mix
+ if (mix.isRoutedToDevice(deviceTypes[i], deviceAddresses[i])) {
+ hasDevice = true;
+ break;
+ }
+ }
+ if (!hasDevice) {
+ return false;
+ }
+ }
+ return true;
+ }
+
void addMixes(@NonNull ArrayList<AudioMix> mixes) {
// TODO optimize to not have to unregister the mixes already in place
synchronized (mMixes) {
@@ -8098,6 +8159,29 @@
AudioSystem.registerPolicyMixes(mMixes, true);
Binder.restoreCallingIdentity(identity);
}
+
+ void setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
+ final Integer Uid = new Integer(uid);
+ if (mUidDeviceAffinities.remove(Uid) != null) {
+ final long identity = Binder.clearCallingIdentity();
+ AudioSystem.removeUidDeviceAffinities(uid);
+ Binder.restoreCallingIdentity(identity);
+ }
+ final long identity = Binder.clearCallingIdentity();
+ final int res = AudioSystem.setUidDeviceAffinities(uid, types, addresses);
+ Binder.restoreCallingIdentity(identity);
+ if (res == AudioSystem.SUCCESS) {
+ mUidDeviceAffinities.put(Uid, new AudioDeviceArray(types, addresses));
+ }
+ }
+
+ void removeUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
+ if (mUidDeviceAffinities.remove(new Integer(uid)) != null) {
+ final long identity = Binder.clearCallingIdentity();
+ AudioSystem.removeUidDeviceAffinities(uid);
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
};
//======================
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index a381477..36ca4dc 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -373,6 +373,13 @@
public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token)
throws RemoteException {
try {
+ // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
+ // after user dismissed/canceled dialog).
+ if (mCurrentAuthSession == null) {
+ Slog.e(TAG, "onAuthenticationSucceeded(): Auth session is null");
+ return;
+ }
+
if (!requireConfirmation) {
mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
KeyStore.getInstance().addAuthToken(token);
@@ -398,6 +405,13 @@
public void onAuthenticationFailed(int cookie, boolean requireConfirmation)
throws RemoteException {
try {
+ // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
+ // after user dismissed/canceled dialog).
+ if (mCurrentAuthSession == null) {
+ Slog.e(TAG, "onAuthenticationFailed(): Auth session is null");
+ return;
+ }
+
mStatusBarService.onBiometricHelp(getContext().getResources().getString(
com.android.internal.R.string.biometric_not_recognized));
if (requireConfirmation) {
@@ -486,6 +500,13 @@
@Override
public void onAcquired(int acquiredInfo, String message) throws RemoteException {
+ // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
+ // after user dismissed/canceled dialog).
+ if (mCurrentAuthSession == null) {
+ Slog.e(TAG, "onAcquired(): Auth session is null");
+ return;
+ }
+
if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
try {
mStatusBarService.onBiometricHelp(message);
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index b6c82d3..73d3d95 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -16,6 +16,7 @@
package com.android.server.display;
+import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
import android.animation.Animator;
@@ -31,6 +32,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
+import android.hardware.display.ColorDisplayManager;
import android.hardware.display.IColorDisplayManager;
import android.net.Uri;
import android.opengl.Matrix;
@@ -86,23 +88,94 @@
*/
private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
+ private final TintController mNightDisplayTintController = new TintController() {
+
+ private float[] mMatrixNightDisplay = new float[16];
+ private final float[] mColorTempCoefficients = new float[9];
+
+ /**
+ * Set coefficients based on whether the color matrix is linear or not.
+ */
+ @Override
+ public void setUp(Context context, boolean needsLinear) {
+ final String[] coefficients = context.getResources().getStringArray(needsLinear
+ ? R.array.config_nightDisplayColorTemperatureCoefficients
+ : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
+ for (int i = 0; i < 9 && i < coefficients.length; i++) {
+ mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
+ }
+ }
+
+ @Override
+ public void setMatrix(int cct) {
+ if (mMatrixNightDisplay.length != 16) {
+ Slog.d(TAG, "The display transformation matrix must be 4x4");
+ return;
+ }
+
+ Matrix.setIdentityM(mMatrixNightDisplay, 0);
+
+ final float squareTemperature = cct * cct;
+ final float red = squareTemperature * mColorTempCoefficients[0]
+ + cct * mColorTempCoefficients[1] + mColorTempCoefficients[2];
+ final float green = squareTemperature * mColorTempCoefficients[3]
+ + cct * mColorTempCoefficients[4] + mColorTempCoefficients[5];
+ final float blue = squareTemperature * mColorTempCoefficients[6]
+ + cct * mColorTempCoefficients[7] + mColorTempCoefficients[8];
+ mMatrixNightDisplay[0] = red;
+ mMatrixNightDisplay[5] = green;
+ mMatrixNightDisplay[10] = blue;
+ }
+
+ @Override
+ public float[] getMatrix() {
+ return isActivated() ? mMatrixNightDisplay : MATRIX_IDENTITY;
+ }
+
+ @Override
+ public int getLevel() {
+ return LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
+ }
+ };
+
+ private final TintController mDisplayWhiteBalanceTintController = new TintController() {
+
+ private float[] mMatrixDisplayWhiteBalance = new float[16];
+
+ @Override
+ public void setUp(Context context, boolean needsLinear) {
+ }
+
+ @Override
+ public float[] getMatrix() {
+ return isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
+ }
+
+ @Override
+ public void setMatrix(int cct) {
+ }
+
+ @Override
+ public int getLevel() {
+ return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
+ }
+ };
+
private final Handler mHandler;
- private float[] mMatrixNight = new float[16];
-
- private final float[] mColorTempCoefficients = new float[9];
-
private int mCurrentUser = UserHandle.USER_NULL;
private ContentObserver mUserSetupObserver;
private boolean mBootCompleted;
private ColorDisplayController mNightDisplayController;
private ContentObserver mContentObserver;
- private ValueAnimator mColorMatrixAnimator;
- private Boolean mIsNightDisplayActivated;
+ private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener;
+
private NightDisplayAutoMode mNightDisplayAutoMode;
+ private Integer mDisplayWhiteBalanceColorTemperature;
+
public ColorDisplayService(Context context) {
super(context);
mHandler = new Handler(Looper.getMainLooper());
@@ -111,6 +184,7 @@
@Override
public void onStart() {
publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
+ publishLocalService(ColorDisplayServiceInternal.class, new ColorDisplayServiceInternal());
}
@Override
@@ -232,6 +306,9 @@
case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
onAccessibilityTransformChanged();
break;
+ case Secure.DISPLAY_WHITE_BALANCE_ENABLED:
+ onDisplayWhiteBalanceEnabled(isDisplayWhiteBalanceSettingEnabled());
+ break;
}
}
}
@@ -256,25 +333,41 @@
cr.registerContentObserver(
Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(Secure.getUriFor(Secure.DISPLAY_WHITE_BALANCE_ENABLED),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
// Set the color mode, if valid, and immediately apply the updated tint matrix based on the
// existing activated state. This ensures consistency of tint across the color mode change.
onDisplayColorModeChanged(mNightDisplayController.getColorMode());
- // Reset the activated state.
- mIsNightDisplayActivated = null;
+ if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
+ // Reset the activated state.
+ mNightDisplayTintController.setActivated(null);
- setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix());
+ // Prepare the night display color transformation matrix.
+ mNightDisplayTintController
+ .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
+ mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature());
- // Prepare color transformation matrix.
- setMatrix(mNightDisplayController.getColorTemperature(), mMatrixNight);
+ // Initialize the current auto mode.
+ onNightDisplayAutoModeChanged(mNightDisplayController.getAutoMode());
- // Initialize the current auto mode.
- onNightDisplayAutoModeChanged(mNightDisplayController.getAutoMode());
+ // Force the initialization current activated state.
+ if (mNightDisplayTintController.isActivatedStateNotSet()) {
+ onNightDisplayActivated(mNightDisplayController.isActivated());
+ }
+ }
- // Force the initialization current activated state.
- if (mIsNightDisplayActivated == null) {
- onNightDisplayActivated(mNightDisplayController.isActivated());
+ if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
+ // Prepare the display white balance transform matrix.
+ mDisplayWhiteBalanceTintController
+ .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
+ if (mDisplayWhiteBalanceColorTemperature != null) {
+ mDisplayWhiteBalanceTintController
+ .setMatrix(mDisplayWhiteBalanceColorTemperature);
+ }
+
+ onDisplayWhiteBalanceEnabled(isDisplayWhiteBalanceSettingEnabled());
}
}
@@ -287,28 +380,31 @@
mNightDisplayController = null;
}
- if (mNightDisplayAutoMode != null) {
- mNightDisplayAutoMode.onStop();
- mNightDisplayAutoMode = null;
+ if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onStop();
+ mNightDisplayAutoMode = null;
+ }
+ mNightDisplayTintController.endAnimator();
}
- if (mColorMatrixAnimator != null) {
- mColorMatrixAnimator.end();
- mColorMatrixAnimator = null;
+ if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
+ mDisplayWhiteBalanceTintController.endAnimator();
}
}
private void onNightDisplayActivated(boolean activated) {
- if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activated) {
+ if (mNightDisplayTintController.isActivatedStateNotSet()
+ || mNightDisplayTintController.isActivated() != activated) {
Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
- mIsNightDisplayActivated = activated;
+ mNightDisplayTintController.setActivated(activated);
if (mNightDisplayAutoMode != null) {
mNightDisplayAutoMode.onActivated(activated);
}
- applyTint(false);
+ applyTint(mNightDisplayTintController, false);
}
}
@@ -348,8 +444,8 @@
}
private void onNightDisplayColorTemperatureChanged(int colorTemperature) {
- setMatrix(colorTemperature, mMatrixNight);
- applyTint(true);
+ mNightDisplayTintController.setMatrix(colorTemperature);
+ applyTint(mNightDisplayTintController, true);
}
private void onDisplayColorModeChanged(int mode) {
@@ -357,66 +453,53 @@
return;
}
- // Cancel the night display tint animator if it's running.
- if (mColorMatrixAnimator != null) {
- mColorMatrixAnimator.cancel();
+ mNightDisplayTintController.cancelAnimator();
+ mDisplayWhiteBalanceTintController.cancelAnimator();
+
+ mNightDisplayTintController
+ .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
+ mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature());
+
+ mDisplayWhiteBalanceTintController
+ .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
+ if (mDisplayWhiteBalanceColorTemperature != null) {
+ mDisplayWhiteBalanceTintController.setMatrix(mDisplayWhiteBalanceColorTemperature);
}
- setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
- setMatrix(mNightDisplayController.getColorTemperature(), mMatrixNight);
-
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
- dtm.setColorMode(mode, (mIsNightDisplayActivated != null && mIsNightDisplayActivated)
- ? mMatrixNight : MATRIX_IDENTITY);
+ dtm.setColorMode(mode, mNightDisplayTintController.getMatrix());
}
private void onAccessibilityTransformChanged() {
onDisplayColorModeChanged(mNightDisplayController.getColorMode());
}
- /**
- * Set coefficients based on whether the color matrix is linear or not.
- */
- private void setCoefficientMatrix(Context context, boolean needsLinear) {
- final String[] coefficients = context.getResources().getStringArray(needsLinear
- ? R.array.config_nightDisplayColorTemperatureCoefficients
- : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
- for (int i = 0; i < 9 && i < coefficients.length; i++) {
- mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
- }
- }
/**
* Applies current color temperature matrix, or removes it if deactivated.
*
* @param immediate {@code true} skips transition animation
*/
- private void applyTint(boolean immediate) {
- // Cancel the old animator if still running.
- if (mColorMatrixAnimator != null) {
- mColorMatrixAnimator.cancel();
- }
+ private void applyTint(TintController tintController, boolean immediate) {
+ tintController.cancelAnimator();
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
- final float[] from = dtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY);
- final float[] to = mIsNightDisplayActivated ? mMatrixNight : MATRIX_IDENTITY;
+ final float[] from = dtm.getColorMatrix(tintController.getLevel());
+ final float[] to = tintController.getMatrix();
if (immediate) {
- dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, to);
+ dtm.setColorMatrix(tintController.getLevel(), to);
} else {
- mColorMatrixAnimator = ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
- from == null ? MATRIX_IDENTITY : from, to);
- mColorMatrixAnimator.setDuration(TRANSITION_DURATION);
- mColorMatrixAnimator.setInterpolator(AnimationUtils.loadInterpolator(
+ tintController.setAnimator(ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
+ from == null ? MATRIX_IDENTITY : from, to));
+ tintController.getAnimator().setDuration(TRANSITION_DURATION);
+ tintController.getAnimator().setInterpolator(AnimationUtils.loadInterpolator(
getContext(), android.R.interpolator.fast_out_slow_in));
- mColorMatrixAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
- final float[] value = (float[]) animator.getAnimatedValue();
- dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, value);
- }
+ tintController.getAnimator().addUpdateListener((ValueAnimator animator) -> {
+ final float[] value = (float[]) animator.getAnimatedValue();
+ dtm.setColorMatrix(tintController.getLevel(), value);
});
- mColorMatrixAnimator.addListener(new AnimatorListenerAdapter() {
+ tintController.getAnimator().addListener(new AnimatorListenerAdapter() {
private boolean mIsCancelled;
@@ -431,42 +514,16 @@
// Ensure final color matrix is set at the end of the animation. If the
// animation is cancelled then don't set the final color matrix so the new
// animator can pick up from where this one left off.
- dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, to);
+ dtm.setColorMatrix(tintController.getLevel(), to);
}
- mColorMatrixAnimator = null;
+ tintController.setAnimator(null);
}
});
- mColorMatrixAnimator.start();
+ tintController.getAnimator().start();
}
}
/**
- * Set the color transformation {@code MATRIX_NIGHT} to the given color temperature.
- *
- * @param colorTemperature color temperature in Kelvin
- * @param outTemp the 4x4 display transformation matrix for that color temperature
- */
- private void setMatrix(int colorTemperature, float[] outTemp) {
- if (outTemp.length != 16) {
- Slog.d(TAG, "The display transformation matrix must be 4x4");
- return;
- }
-
- Matrix.setIdentityM(mMatrixNight, 0);
-
- final float squareTemperature = colorTemperature * colorTemperature;
- final float red = squareTemperature * mColorTempCoefficients[0]
- + colorTemperature * mColorTempCoefficients[1] + mColorTempCoefficients[2];
- final float green = squareTemperature * mColorTempCoefficients[3]
- + colorTemperature * mColorTempCoefficients[4] + mColorTempCoefficients[5];
- final float blue = squareTemperature * mColorTempCoefficients[6]
- + colorTemperature * mColorTempCoefficients[7] + mColorTempCoefficients[8];
- outTemp[0] = red;
- outTemp[5] = green;
- outTemp[10] = blue;
- }
-
- /**
* Returns the first date time corresponding to the local time that occurs before the provided
* date time.
*
@@ -498,6 +555,18 @@
return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
}
+ private void onDisplayWhiteBalanceEnabled(boolean enabled) {
+ mDisplayWhiteBalanceTintController.setActivated(enabled);
+ if (mDisplayWhiteBalanceListener != null) {
+ mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(enabled);
+ }
+ }
+
+ private boolean isDisplayWhiteBalanceSettingEnabled() {
+ return Secure.getIntForUser(getContext().getContentResolver(),
+ Secure.DISPLAY_WHITE_BALANCE_ENABLED, 0, mCurrentUser) == 1;
+ }
+
private boolean isDeviceColorManagedInternal() {
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
return dtm.isDeviceColorManaged();
@@ -507,7 +576,7 @@
* Returns the last time the night display transform activation state was changed, or {@link
* LocalDateTime#MIN} if night display has never been activated.
*/
- private @NonNull LocalDateTime getNightDisplayLastActivatedTimeSetting() {
+ private LocalDateTime getNightDisplayLastActivatedTimeSetting() {
final ContentResolver cr = getContext().getContentResolver();
final String lastActivatedTime = Secure.getStringForUser(
cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId());
@@ -577,11 +646,12 @@
}
}
- if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activate) {
+ if (mNightDisplayTintController.isActivatedStateNotSet() || (
+ mNightDisplayTintController.isActivated() != activate)) {
mNightDisplayController.setActivated(activate);
}
- updateNextAlarm(mIsNightDisplayActivated, now);
+ updateNextAlarm(mNightDisplayTintController.isActivated(), now);
}
private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
@@ -672,7 +742,8 @@
}
}
- if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activate) {
+ if (mNightDisplayTintController.isActivatedStateNotSet() || (
+ mNightDisplayTintController.isActivated() != activate)) {
mNightDisplayController.setActivated(activate);
}
}
@@ -724,6 +795,115 @@
}
}
+ private abstract static class TintController {
+
+ private ValueAnimator mAnimator;
+ private Boolean mIsActivated;
+
+ public ValueAnimator getAnimator() {
+ return mAnimator;
+ }
+
+ public void setAnimator(ValueAnimator animator) {
+ mAnimator = animator;
+ }
+
+ /**
+ * Cancel the animator if it's still running.
+ */
+ public void cancelAnimator() {
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
+ }
+
+ /**
+ * End the animator if it's still running, jumping to the end state.
+ */
+ public void endAnimator() {
+ if (mAnimator != null) {
+ mAnimator.end();
+ mAnimator = null;
+ }
+ }
+
+ public void setActivated(Boolean isActivated) {
+ mIsActivated = isActivated;
+ }
+
+ public boolean isActivated() {
+ return mIsActivated != null && mIsActivated;
+ }
+
+ public boolean isActivatedStateNotSet() {
+ return mIsActivated == null;
+ }
+
+ /**
+ * Set up any constants needed for computing the matrix.
+ */
+ public abstract void setUp(Context context, boolean needsLinear);
+
+ /**
+ * Sets the 4x4 matrix to apply.
+ */
+ public abstract void setMatrix(int value);
+
+ /**
+ * Get the 4x4 matrix to apply.
+ */
+ public abstract float[] getMatrix();
+
+ /**
+ * Get the color transform level to apply the matrix.
+ */
+ public abstract int getLevel();
+ }
+
+ /**
+ * Local service that allows color transforms to be enabled from other system services.
+ */
+ public final class ColorDisplayServiceInternal {
+
+ /**
+ * Set the current CCT value for the display white balance transform, and if the transform
+ * is enabled, apply it.
+ *
+ * @param cct the color temperature in Kelvin.
+ */
+ public boolean setDisplayWhiteBalanceColorTemperature(int cct) {
+ // Update the transform matrix even if it can't be applied.
+ mDisplayWhiteBalanceColorTemperature = cct;
+ mDisplayWhiteBalanceTintController.setMatrix(cct);
+
+ if (mDisplayWhiteBalanceTintController.isActivated()) {
+ applyTint(mDisplayWhiteBalanceTintController, true);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Sets the listener and returns whether display white balance is currently enabled.
+ */
+ public boolean setDisplayWhiteBalanceListener(DisplayWhiteBalanceListener listener) {
+ mDisplayWhiteBalanceListener = listener;
+ return mDisplayWhiteBalanceTintController.isActivated();
+ }
+ }
+
+ /**
+ * Listener for changes in display white balance status.
+ */
+ public interface DisplayWhiteBalanceListener {
+
+ /**
+ * Notify that the display white balance status has changed, either due to preemption by
+ * another transform or the feature being turned off.
+ */
+ void onDisplayWhiteBalanceStatusChanged(boolean enabled);
+ }
+
private final class BinderService extends IColorDisplayManager.Stub {
@Override
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index 4ad26da..a5e9728 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -45,6 +45,10 @@
*/
public static final int LEVEL_COLOR_MATRIX_NIGHT_DISPLAY = 100;
/**
+ * Color transform level used by display white balance to adjust the display's white point.
+ */
+ public static final int LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE = 125;
+ /**
* Color transform level used to adjust the color saturation of the display.
*/
public static final int LEVEL_COLOR_MATRIX_SATURATION = 150;
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index dd2abde..139d8ac 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -19,6 +19,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
@@ -26,16 +27,15 @@
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
-import android.media.session.ControllerCallbackLink;
import android.media.session.ISession;
+import android.media.session.ISessionCallback;
import android.media.session.ISessionController;
+import android.media.session.ISessionControllerCallback;
import android.media.session.MediaController;
import android.media.session.MediaController.PlaybackInfo;
import android.media.session.MediaSession;
-import android.media.session.MediaSession.QueueItem;
import android.media.session.ParcelableVolumeInfo;
import android.media.session.PlaybackState;
-import android.media.session.SessionCallbackLink;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -45,6 +45,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.Process;
+import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.util.Log;
@@ -55,7 +56,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.List;
/**
* This is the system implementation of a Session. Apps will interact with the
@@ -85,7 +85,7 @@
private final Context mContext;
private final Object mLock = new Object();
- private final ArrayList<ControllerCallbackLinkHolder> mControllerCallbackHolders =
+ private final ArrayList<ISessionControllerCallbackHolder> mControllerCallbackHolders =
new ArrayList<>();
private long mFlags;
@@ -98,7 +98,7 @@
// may result in throwing an exception.
private MediaMetadata mMetadata;
private PlaybackState mPlaybackState;
- private List<QueueItem> mQueue;
+ private ParceledListSlice mQueue;
private CharSequence mQueueTitle;
private int mRatingType;
// End TransportPerformer fields
@@ -121,7 +121,7 @@
private String mMetadataDescription;
public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
- SessionCallbackLink cb, String tag, MediaSessionService service, Looper handlerLooper) {
+ ISessionCallback cb, String tag, MediaSessionService service, Looper handlerLooper) {
mOwnerPid = ownerPid;
mOwnerUid = ownerUid;
mUserId = userId;
@@ -236,6 +236,7 @@
* {@link AudioManager#ADJUST_SAME}.
*
* @param packageName The package that made the original volume request.
+ * @param opPackageName The op package that made the original volume request.
* @param pid The pid that made the original volume request.
* @param uid The uid that made the original volume request.
* @param caller caller binder. can be {@code null} if it's from the volume key.
@@ -248,8 +249,8 @@
* @param flags Any of the flags from {@link AudioManager}.
* @param useSuggested True to use adjustSuggestedStreamVolume instead of
*/
- public void adjustVolume(String packageName, int pid, int uid,
- ControllerCallbackLink caller, boolean asSystemService, int direction, int flags,
+ public void adjustVolume(String packageName, String opPackageName, int pid, int uid,
+ ISessionControllerCallback caller, boolean asSystemService, int direction, int flags,
boolean useSuggested) {
int previousFlagPlaySound = flags & AudioManager.FLAG_PLAY_SOUND;
if (isPlaybackActive() || hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
@@ -258,8 +259,8 @@
if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
// Adjust the volume with a handler not to be blocked by other system service.
int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
- postAdjustLocalVolume(stream, direction, flags, packageName, uid, asSystemService,
- useSuggested, previousFlagPlaySound);
+ postAdjustLocalVolume(stream, direction, flags, opPackageName, pid, uid,
+ asSystemService, useSuggested, previousFlagPlaySound);
} else {
if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
// Nothing to do, the volume cannot be changed
@@ -290,11 +291,23 @@
}
}
- private void setVolumeTo(String packageName, int pid, int uid,
- ControllerCallbackLink caller, int value, int flags) {
+ private void setVolumeTo(String packageName, String opPackageName, int pid, int uid,
+ ISessionControllerCallback caller, int value, int flags) {
if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
- mAudioManagerInternal.setStreamVolumeForUid(stream, value, flags, packageName, uid);
+ final int volumeValue = value;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mAudioManagerInternal.setStreamVolumeForUid(stream, volumeValue, flags,
+ opPackageName, uid);
+ } catch (IllegalArgumentException | SecurityException e) {
+ Log.e(TAG, "Cannot set volume: stream=" + stream + ", value=" + volumeValue
+ + ", flags=" + flags, e);
+ }
+ }
+ });
} else {
if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
// Nothing to do. The volume can't be set directly.
@@ -428,7 +441,7 @@
}
}
- public SessionCallbackLink getCallback() {
+ public ISessionCallback getCallback() {
return mSessionCb.mCb;
}
@@ -456,7 +469,7 @@
+ ", max=" + mMaxVolume + ", current=" + mCurrentVolume);
pw.println(indent + "metadata: " + mMetadataDescription);
pw.println(indent + "queueTitle=" + mQueueTitle + ", size="
- + (mQueue == null ? 0 : mQueue.size()));
+ + (mQueue == null ? 0 : mQueue.getList().size()));
}
@Override
@@ -465,11 +478,19 @@
}
private void postAdjustLocalVolume(final int stream, final int direction, final int flags,
- final String callingPackageName, final int callingUid, final boolean asSystemService,
- final boolean useSuggested, final int previousFlagPlaySound) {
- final String packageName = asSystemService
- ? mContext.getOpPackageName() : callingPackageName;
- final int uid = asSystemService ? Process.SYSTEM_UID : callingUid;
+ final String callingOpPackageName, final int callingPid, final int callingUid,
+ final boolean asSystemService, final boolean useSuggested,
+ final int previousFlagPlaySound) {
+ // Must use opPackageName for adjusting volumes with UID.
+ final String opPackageName;
+ final int uid;
+ if (asSystemService) {
+ opPackageName = mContext.getOpPackageName();
+ uid = Process.SYSTEM_UID;
+ } else {
+ opPackageName = callingOpPackageName;
+ uid = callingUid;
+ }
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -477,19 +498,19 @@
if (useSuggested) {
if (AudioSystem.isStreamActive(stream, 0)) {
mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream,
- direction, flags, packageName, uid);
+ direction, flags, opPackageName, uid);
} else {
mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
AudioManager.USE_DEFAULT_STREAM_TYPE, direction,
- flags | previousFlagPlaySound, packageName, uid);
+ flags | previousFlagPlaySound, opPackageName, uid);
}
} else {
mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
- packageName, uid);
+ opPackageName, uid);
}
} catch (IllegalArgumentException | SecurityException e) {
Log.e(TAG, "Cannot adjust volume: direction=" + direction + ", stream="
- + stream + ", flags=" + flags + ", packageName=" + packageName
+ + stream + ", flags=" + flags + ", opPackageName=" + opPackageName
+ ", uid=" + uid + ", useSuggested=" + useSuggested
+ ", previousFlagPlaySound=" + previousFlagPlaySound, e);
}
@@ -498,7 +519,7 @@
}
private void logCallbackException(
- String msg, ControllerCallbackLinkHolder holder, Exception e) {
+ String msg, ISessionControllerCallbackHolder holder, Exception e) {
Log.v(TAG, msg + ", this=" + this + ", callback package=" + holder.mPackageName
+ ", exception=" + e);
}
@@ -509,18 +530,16 @@
return;
}
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
+ ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.notifyPlaybackStateChanged(mPlaybackState);
- } catch (RuntimeException e) {
- if (e.getCause() instanceof DeadObjectException) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushPlaybackStateUpdate",
- holder, e);
- } else {
- logCallbackException("unexpected exception in pushPlaybackStateUpdate",
- holder, e);
- }
+ holder.mCallback.onPlaybackStateChanged(mPlaybackState);
+ } catch (DeadObjectException e) {
+ mControllerCallbackHolders.remove(i);
+ logCallbackException("Removed dead callback in pushPlaybackStateUpdate",
+ holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushPlaybackStateUpdate",
+ holder, e);
}
}
}
@@ -532,18 +551,14 @@
return;
}
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
+ ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.notifyMetadataChanged(mMetadata);
- } catch (RuntimeException e) {
- if (e.getCause() instanceof DeadObjectException) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushMetadataUpdate",
- holder, e);
- } else {
- logCallbackException("unexpected exception in pushMetadataUpdate",
- holder, e);
- }
+ holder.mCallback.onMetadataChanged(mMetadata);
+ } catch (DeadObjectException e) {
+ logCallbackException("Removing dead callback in pushMetadataUpdate", holder, e);
+ mControllerCallbackHolders.remove(i);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushMetadataUpdate", holder, e);
}
}
}
@@ -555,17 +570,14 @@
return;
}
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
+ ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.notifyQueueChanged(mQueue);
- } catch (RuntimeException e) {
- if (e.getCause() instanceof DeadObjectException) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushQueueUpdate",
- holder, e);
- } else {
- logCallbackException("unexpected exception in pushQueueUpdate", holder, e);
- }
+ holder.mCallback.onQueueChanged(mQueue);
+ } catch (DeadObjectException e) {
+ mControllerCallbackHolders.remove(i);
+ logCallbackException("Removed dead callback in pushQueueUpdate", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushQueueUpdate", holder, e);
}
}
}
@@ -577,18 +589,16 @@
return;
}
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
+ ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.notifyQueueTitleChanged(mQueueTitle);
- } catch (RuntimeException e) {
- if (e.getCause() instanceof DeadObjectException) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushQueueTitleUpdate",
- holder, e);
- } else {
- logCallbackException("unexpected exception in pushQueueTitleUpdate",
- holder, e);
- }
+ holder.mCallback.onQueueTitleChanged(mQueueTitle);
+ } catch (DeadObjectException e) {
+ mControllerCallbackHolders.remove(i);
+ logCallbackException("Removed dead callback in pushQueueTitleUpdate",
+ holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushQueueTitleUpdate",
+ holder, e);
}
}
}
@@ -600,17 +610,14 @@
return;
}
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
+ ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.notifyExtrasChanged(mExtras);
- } catch (RuntimeException e) {
- if (e.getCause() instanceof DeadObjectException) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushExtrasUpdate",
- holder, e);
- } else {
- logCallbackException("unexpected exception in pushExtrasUpdate", holder, e);
- }
+ holder.mCallback.onExtrasChanged(mExtras);
+ } catch (DeadObjectException e) {
+ mControllerCallbackHolders.remove(i);
+ logCallbackException("Removed dead callback in pushExtrasUpdate", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushExtrasUpdate", holder, e);
}
}
}
@@ -623,18 +630,14 @@
}
ParcelableVolumeInfo info = mController.getVolumeAttributes();
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
+ ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.notifyVolumeInfoChanged(info.volumeType, info.audioAttrs,
- info.controlType, info.maxVolume, info.currentVolume);
- } catch (RuntimeException e) {
- if (e.getCause() instanceof DeadObjectException) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushVolumeUpdate",
- holder, e);
- } else {
- logCallbackException("unexpected exception in pushVolumeUpdate", holder, e);
- }
+ holder.mCallback.onVolumeInfoChanged(info);
+ } catch (DeadObjectException e) {
+ mControllerCallbackHolders.remove(i);
+ logCallbackException("Removing dead callback in pushVolumeUpdate", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("Unexpected exception in pushVolumeUpdate", holder, e);
}
}
}
@@ -646,16 +649,14 @@
return;
}
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
+ ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.notifyEvent(event, data);
- } catch (RuntimeException e) {
- if (e.getCause() instanceof DeadObjectException) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushEvent", holder, e);
- } else {
- logCallbackException("unexpected exception in pushEvent", holder, e);
- }
+ holder.mCallback.onEvent(event, data);
+ } catch (DeadObjectException e) {
+ mControllerCallbackHolders.remove(i);
+ logCallbackException("Removing dead callback in pushEvent", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushEvent", holder, e);
}
}
}
@@ -669,18 +670,14 @@
return;
}
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
+ ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.notifySessionDestroyed();
- } catch (RuntimeException e) {
- if (e.getCause() instanceof DeadObjectException) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushSessionDestroyed",
- holder, e);
- } else {
- logCallbackException("unexpected exception in pushSessionDestroyed",
- holder, e);
- }
+ holder.mCallback.onSessionDestroyed();
+ } catch (DeadObjectException e) {
+ logCallbackException("Removing dead callback in pushEvent", holder, e);
+ mControllerCallbackHolders.remove(i);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushEvent", holder, e);
}
}
// After notifying clear all listeners
@@ -720,10 +717,10 @@
return result == null ? state : result;
}
- private int getControllerHolderIndexForCb(ControllerCallbackLink cb) {
- IBinder binder = cb.getBinder();
+ private int getControllerHolderIndexForCb(ISessionControllerCallback cb) {
+ IBinder binder = cb.asBinder();
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- if (binder.equals(mControllerCallbackHolders.get(i).mCallback.getBinder())) {
+ if (binder.equals(mControllerCallbackHolders.get(i).mCallback.asBinder())) {
return i;
}
}
@@ -847,7 +844,7 @@
}
@Override
- public void setQueue(List<QueueItem> queue) {
+ public void setQueue(ParceledListSlice queue) {
synchronized (mLock) {
mQueue = queue;
}
@@ -924,9 +921,9 @@
}
class SessionCb {
- private final SessionCallbackLink mCb;
+ private final ISessionCallback mCb;
- SessionCb(SessionCallbackLink cb) {
+ public SessionCb(ISessionCallback cb) {
mCb = cb;
}
@@ -934,224 +931,224 @@
boolean asSystemService, KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
try {
if (asSystemService) {
- mCb.notifyMediaButton(mContext.getPackageName(), Process.myPid(),
+ mCb.onMediaButton(mContext.getPackageName(), Process.myPid(),
Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), sequenceId, cb);
} else {
- mCb.notifyMediaButton(packageName, pid, uid,
+ mCb.onMediaButton(packageName, pid, uid,
createMediaButtonIntent(keyEvent), sequenceId, cb);
}
return true;
- } catch (RuntimeException e) {
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
}
return false;
}
public boolean sendMediaButton(String packageName, int pid, int uid,
- ControllerCallbackLink caller, boolean asSystemService,
+ ISessionControllerCallback caller, boolean asSystemService,
KeyEvent keyEvent) {
try {
if (asSystemService) {
- mCb.notifyMediaButton(mContext.getPackageName(), Process.myPid(),
+ mCb.onMediaButton(mContext.getPackageName(), Process.myPid(),
Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), 0, null);
} else {
- mCb.notifyMediaButtonFromController(packageName, pid, uid, caller,
+ mCb.onMediaButtonFromController(packageName, pid, uid, caller,
createMediaButtonIntent(keyEvent));
}
return true;
- } catch (RuntimeException e) {
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
}
return false;
}
public void sendCommand(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String command, Bundle args, ResultReceiver cb) {
+ ISessionControllerCallback caller, String command, Bundle args, ResultReceiver cb) {
try {
- mCb.notifyCommand(packageName, pid, uid, caller, command, args, cb);
- } catch (RuntimeException e) {
+ mCb.onCommand(packageName, pid, uid, caller, command, args, cb);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in sendCommand.", e);
}
}
public void sendCustomAction(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String action,
+ ISessionControllerCallback caller, String action,
Bundle args) {
try {
- mCb.notifyCustomAction(packageName, pid, uid, caller, action, args);
- } catch (RuntimeException e) {
+ mCb.onCustomAction(packageName, pid, uid, caller, action, args);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in sendCustomAction.", e);
}
}
public void prepare(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
+ ISessionControllerCallback caller) {
try {
- mCb.notifyPrepare(packageName, pid, uid, caller);
- } catch (RuntimeException e) {
+ mCb.onPrepare(packageName, pid, uid, caller);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in prepare.", e);
}
}
public void prepareFromMediaId(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String mediaId, Bundle extras) {
+ ISessionControllerCallback caller, String mediaId, Bundle extras) {
try {
- mCb.notifyPrepareFromMediaId(packageName, pid, uid, caller, mediaId, extras);
- } catch (RuntimeException e) {
+ mCb.onPrepareFromMediaId(packageName, pid, uid, caller, mediaId, extras);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in prepareFromMediaId.", e);
}
}
public void prepareFromSearch(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String query, Bundle extras) {
+ ISessionControllerCallback caller, String query, Bundle extras) {
try {
- mCb.notifyPrepareFromSearch(packageName, pid, uid, caller, query, extras);
- } catch (RuntimeException e) {
+ mCb.onPrepareFromSearch(packageName, pid, uid, caller, query, extras);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in prepareFromSearch.", e);
}
}
public void prepareFromUri(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Uri uri, Bundle extras) {
+ ISessionControllerCallback caller, Uri uri, Bundle extras) {
try {
- mCb.notifyPrepareFromUri(packageName, pid, uid, caller, uri, extras);
- } catch (RuntimeException e) {
+ mCb.onPrepareFromUri(packageName, pid, uid, caller, uri, extras);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in prepareFromUri.", e);
}
}
- public void play(String packageName, int pid, int uid, ControllerCallbackLink caller) {
+ public void play(String packageName, int pid, int uid, ISessionControllerCallback caller) {
try {
- mCb.notifyPlay(packageName, pid, uid, caller);
- } catch (RuntimeException e) {
+ mCb.onPlay(packageName, pid, uid, caller);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in play.", e);
}
}
public void playFromMediaId(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String mediaId, Bundle extras) {
+ ISessionControllerCallback caller, String mediaId, Bundle extras) {
try {
- mCb.notifyPlayFromMediaId(packageName, pid, uid, caller, mediaId, extras);
- } catch (RuntimeException e) {
+ mCb.onPlayFromMediaId(packageName, pid, uid, caller, mediaId, extras);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in playFromMediaId.", e);
}
}
public void playFromSearch(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String query, Bundle extras) {
+ ISessionControllerCallback caller, String query, Bundle extras) {
try {
- mCb.notifyPlayFromSearch(packageName, pid, uid, caller, query, extras);
- } catch (RuntimeException e) {
+ mCb.onPlayFromSearch(packageName, pid, uid, caller, query, extras);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in playFromSearch.", e);
}
}
public void playFromUri(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Uri uri, Bundle extras) {
+ ISessionControllerCallback caller, Uri uri, Bundle extras) {
try {
- mCb.notifyPlayFromUri(packageName, pid, uid, caller, uri, extras);
- } catch (RuntimeException e) {
+ mCb.onPlayFromUri(packageName, pid, uid, caller, uri, extras);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in playFromUri.", e);
}
}
public void skipToTrack(String packageName, int pid, int uid,
- ControllerCallbackLink caller, long id) {
+ ISessionControllerCallback caller, long id) {
try {
- mCb.notifySkipToTrack(packageName, pid, uid, caller, id);
- } catch (RuntimeException e) {
+ mCb.onSkipToTrack(packageName, pid, uid, caller, id);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in skipToTrack", e);
}
}
- public void pause(String packageName, int pid, int uid, ControllerCallbackLink caller) {
+ public void pause(String packageName, int pid, int uid, ISessionControllerCallback caller) {
try {
- mCb.notifyPause(packageName, pid, uid, caller);
- } catch (RuntimeException e) {
+ mCb.onPause(packageName, pid, uid, caller);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in pause.", e);
}
}
- public void stop(String packageName, int pid, int uid, ControllerCallbackLink caller) {
+ public void stop(String packageName, int pid, int uid, ISessionControllerCallback caller) {
try {
- mCb.notifyStop(packageName, pid, uid, caller);
- } catch (RuntimeException e) {
+ mCb.onStop(packageName, pid, uid, caller);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in stop.", e);
}
}
- public void next(String packageName, int pid, int uid, ControllerCallbackLink caller) {
+ public void next(String packageName, int pid, int uid, ISessionControllerCallback caller) {
try {
- mCb.notifyNext(packageName, pid, uid, caller);
- } catch (RuntimeException e) {
+ mCb.onNext(packageName, pid, uid, caller);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in next.", e);
}
}
public void previous(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
+ ISessionControllerCallback caller) {
try {
- mCb.notifyPrevious(packageName, pid, uid, caller);
- } catch (RuntimeException e) {
+ mCb.onPrevious(packageName, pid, uid, caller);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in previous.", e);
}
}
public void fastForward(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
+ ISessionControllerCallback caller) {
try {
- mCb.notifyFastForward(packageName, pid, uid, caller);
- } catch (RuntimeException e) {
+ mCb.onFastForward(packageName, pid, uid, caller);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in fastForward.", e);
}
}
public void rewind(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
+ ISessionControllerCallback caller) {
try {
- mCb.notifyRewind(packageName, pid, uid, caller);
- } catch (RuntimeException e) {
+ mCb.onRewind(packageName, pid, uid, caller);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in rewind.", e);
}
}
- public void seekTo(String packageName, int pid, int uid, ControllerCallbackLink caller,
+ public void seekTo(String packageName, int pid, int uid, ISessionControllerCallback caller,
long pos) {
try {
- mCb.notifySeekTo(packageName, pid, uid, caller, pos);
- } catch (RuntimeException e) {
+ mCb.onSeekTo(packageName, pid, uid, caller, pos);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in seekTo.", e);
}
}
- public void rate(String packageName, int pid, int uid, ControllerCallbackLink caller,
+ public void rate(String packageName, int pid, int uid, ISessionControllerCallback caller,
Rating rating) {
try {
- mCb.notifyRate(packageName, pid, uid, caller, rating);
- } catch (RuntimeException e) {
+ mCb.onRate(packageName, pid, uid, caller, rating);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in rate.", e);
}
}
public void adjustVolume(String packageName, int pid, int uid,
- ControllerCallbackLink caller, boolean asSystemService, int direction) {
+ ISessionControllerCallback caller, boolean asSystemService, int direction) {
try {
if (asSystemService) {
- mCb.notifyAdjustVolume(mContext.getPackageName(), Process.myPid(),
+ mCb.onAdjustVolume(mContext.getPackageName(), Process.myPid(),
Process.SYSTEM_UID, null, direction);
} else {
- mCb.notifyAdjustVolume(packageName, pid, uid, caller, direction);
+ mCb.onAdjustVolume(packageName, pid, uid, caller, direction);
}
- } catch (RuntimeException e) {
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in adjustVolume.", e);
}
}
public void setVolumeTo(String packageName, int pid, int uid,
- ControllerCallbackLink caller, int value) {
+ ISessionControllerCallback caller, int value) {
try {
- mCb.notifySetVolumeTo(packageName, pid, uid, caller, value);
- } catch (RuntimeException e) {
+ mCb.onSetVolumeTo(packageName, pid, uid, caller, value);
+ } catch (RemoteException e) {
Slog.e(TAG, "Remote failure in setVolumeTo.", e);
}
}
@@ -1165,34 +1162,34 @@
class ControllerStub extends ISessionController.Stub {
@Override
- public void sendCommand(String packageName, ControllerCallbackLink caller,
+ public void sendCommand(String packageName, ISessionControllerCallback caller,
String command, Bundle args, ResultReceiver cb) {
mSessionCb.sendCommand(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller, command, args, cb);
}
@Override
- public boolean sendMediaButton(String packageName, ControllerCallbackLink cb,
+ public boolean sendMediaButton(String packageName, ISessionControllerCallback cb,
boolean asSystemService, KeyEvent keyEvent) {
return mSessionCb.sendMediaButton(packageName, Binder.getCallingPid(),
Binder.getCallingUid(), cb, asSystemService, keyEvent);
}
@Override
- public void registerCallbackListener(String packageName, ControllerCallbackLink cb) {
+ public void registerCallbackListener(String packageName, ISessionControllerCallback cb) {
synchronized (mLock) {
// If this session is already destroyed tell the caller and
// don't add them.
if (mDestroyed) {
try {
- cb.notifySessionDestroyed();
+ cb.onSessionDestroyed();
} catch (Exception e) {
// ignored
}
return;
}
if (getControllerHolderIndexForCb(cb) < 0) {
- mControllerCallbackHolders.add(new ControllerCallbackLinkHolder(cb,
+ mControllerCallbackHolders.add(new ISessionControllerCallbackHolder(cb,
packageName, Binder.getCallingUid()));
if (DEBUG) {
Log.d(TAG, "registering controller callback " + cb + " from controller"
@@ -1203,14 +1200,14 @@
}
@Override
- public void unregisterCallbackListener(ControllerCallbackLink cb) {
+ public void unregisterCallbackListener(ISessionControllerCallback cb) {
synchronized (mLock) {
int index = getControllerHolderIndexForCb(cb);
if (index != -1) {
mControllerCallbackHolders.remove(index);
}
if (DEBUG) {
- Log.d(TAG, "unregistering callback " + cb.getBinder());
+ Log.d(TAG, "unregistering callback " + cb.asBinder());
}
}
}
@@ -1256,137 +1253,139 @@
}
@Override
- public void adjustVolume(String packageName, ControllerCallbackLink caller,
- boolean asSystemService, int direction, int flags) {
+ public void adjustVolume(String packageName, String opPackageName,
+ ISessionControllerCallback caller, boolean asSystemService, int direction,
+ int flags) {
int pid = Binder.getCallingPid();
int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- MediaSessionRecord.this.adjustVolume(packageName, pid, uid, caller, asSystemService,
- direction, flags, false /* useSuggested */);
+ MediaSessionRecord.this.adjustVolume(packageName, opPackageName, pid, uid, caller,
+ asSystemService, direction, flags, false /* useSuggested */);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
- public void setVolumeTo(String packageName, ControllerCallbackLink caller,
- int value, int flags) {
+ public void setVolumeTo(String packageName, String opPackageName,
+ ISessionControllerCallback caller, int value, int flags) {
int pid = Binder.getCallingPid();
int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- MediaSessionRecord.this.setVolumeTo(packageName, pid, uid, caller, value, flags);
+ MediaSessionRecord.this.setVolumeTo(packageName, opPackageName, pid, uid, caller,
+ value, flags);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
- public void prepare(String packageName, ControllerCallbackLink caller) {
+ public void prepare(String packageName, ISessionControllerCallback caller) {
mSessionCb.prepare(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
}
@Override
- public void prepareFromMediaId(String packageName, ControllerCallbackLink caller,
+ public void prepareFromMediaId(String packageName, ISessionControllerCallback caller,
String mediaId, Bundle extras) {
mSessionCb.prepareFromMediaId(packageName, Binder.getCallingPid(),
Binder.getCallingUid(), caller, mediaId, extras);
}
@Override
- public void prepareFromSearch(String packageName, ControllerCallbackLink caller,
+ public void prepareFromSearch(String packageName, ISessionControllerCallback caller,
String query, Bundle extras) {
mSessionCb.prepareFromSearch(packageName, Binder.getCallingPid(),
Binder.getCallingUid(), caller, query, extras);
}
@Override
- public void prepareFromUri(String packageName, ControllerCallbackLink caller,
+ public void prepareFromUri(String packageName, ISessionControllerCallback caller,
Uri uri, Bundle extras) {
mSessionCb.prepareFromUri(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller, uri, extras);
}
@Override
- public void play(String packageName, ControllerCallbackLink caller) {
+ public void play(String packageName, ISessionControllerCallback caller) {
mSessionCb.play(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
}
@Override
- public void playFromMediaId(String packageName, ControllerCallbackLink caller,
+ public void playFromMediaId(String packageName, ISessionControllerCallback caller,
String mediaId, Bundle extras) {
mSessionCb.playFromMediaId(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller, mediaId, extras);
}
@Override
- public void playFromSearch(String packageName, ControllerCallbackLink caller,
+ public void playFromSearch(String packageName, ISessionControllerCallback caller,
String query, Bundle extras) {
mSessionCb.playFromSearch(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller, query, extras);
}
@Override
- public void playFromUri(String packageName, ControllerCallbackLink caller,
+ public void playFromUri(String packageName, ISessionControllerCallback caller,
Uri uri, Bundle extras) {
mSessionCb.playFromUri(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller, uri, extras);
}
@Override
- public void skipToQueueItem(String packageName, ControllerCallbackLink caller,
+ public void skipToQueueItem(String packageName, ISessionControllerCallback caller,
long id) {
mSessionCb.skipToTrack(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller, id);
}
@Override
- public void pause(String packageName, ControllerCallbackLink caller) {
+ public void pause(String packageName, ISessionControllerCallback caller) {
mSessionCb.pause(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
}
@Override
- public void stop(String packageName, ControllerCallbackLink caller) {
+ public void stop(String packageName, ISessionControllerCallback caller) {
mSessionCb.stop(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
}
@Override
- public void next(String packageName, ControllerCallbackLink caller) {
+ public void next(String packageName, ISessionControllerCallback caller) {
mSessionCb.next(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
}
@Override
- public void previous(String packageName, ControllerCallbackLink caller) {
+ public void previous(String packageName, ISessionControllerCallback caller) {
mSessionCb.previous(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller);
}
@Override
- public void fastForward(String packageName, ControllerCallbackLink caller) {
+ public void fastForward(String packageName, ISessionControllerCallback caller) {
mSessionCb.fastForward(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller);
}
@Override
- public void rewind(String packageName, ControllerCallbackLink caller) {
+ public void rewind(String packageName, ISessionControllerCallback caller) {
mSessionCb.rewind(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
}
@Override
- public void seekTo(String packageName, ControllerCallbackLink caller, long pos) {
+ public void seekTo(String packageName, ISessionControllerCallback caller, long pos) {
mSessionCb.seekTo(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller,
pos);
}
@Override
- public void rate(String packageName, ControllerCallbackLink caller, Rating rating) {
+ public void rate(String packageName, ISessionControllerCallback caller, Rating rating) {
mSessionCb.rate(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller,
rating);
}
@Override
- public void sendCustomAction(String packageName, ControllerCallbackLink caller,
+ public void sendCustomAction(String packageName, ISessionControllerCallback caller,
String action, Bundle args) {
mSessionCb.sendCustomAction(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller, action, args);
@@ -1405,7 +1404,7 @@
}
@Override
- public List<QueueItem> getQueue() {
+ public ParceledListSlice getQueue() {
synchronized (mLock) {
return mQueue;
}
@@ -1434,12 +1433,12 @@
}
}
- private class ControllerCallbackLinkHolder {
- private final ControllerCallbackLink mCallback;
+ private class ISessionControllerCallbackHolder {
+ private final ISessionControllerCallback mCallback;
private final String mPackageName;
private final int mUid;
- ControllerCallbackLinkHolder(ControllerCallbackLink callback, String packageName,
+ ISessionControllerCallbackHolder(ISessionControllerCallback callback, String packageName,
int uid) {
mCallback = callback;
mPackageName = packageName;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index b807c47..d51da99 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -35,6 +35,7 @@
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.media.AudioManager;
+import android.media.AudioManagerInternal;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioSystem;
import android.media.IAudioService;
@@ -44,10 +45,10 @@
import android.media.session.IOnMediaKeyListener;
import android.media.session.IOnVolumeKeyLongPressListener;
import android.media.session.ISession;
+import android.media.session.ISessionCallback;
import android.media.session.ISessionManager;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
-import android.media.session.SessionCallbackLink;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -72,6 +73,7 @@
import android.view.ViewConfiguration;
import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.Watchdog;
import com.android.server.Watchdog.Monitor;
@@ -108,6 +110,8 @@
private KeyguardManager mKeyguardManager;
private IAudioService mAudioService;
+ private AudioManagerInternal mAudioManagerInternal;
+ private ActivityManager mActivityManager;
private ContentResolver mContentResolver;
private SettingsObserver mSettingsObserver;
private boolean mHasFeatureLeanback;
@@ -139,6 +143,9 @@
mKeyguardManager =
(KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
mAudioService = getAudioService();
+ mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
+ mActivityManager =
+ (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);
mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
mAudioPlayerStateMonitor.registerListener(
(config, isRemoved) -> {
@@ -407,7 +414,7 @@
}
try {
- session.getCallback().getBinder().unlinkToDeath(session, 0);
+ session.getCallback().asBinder().unlinkToDeath(session, 0);
} catch (Exception e) {
// ignore exceptions while destroying a session.
}
@@ -493,7 +500,7 @@
}
private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
- String callerPackageName, SessionCallbackLink cb, String tag) throws RemoteException {
+ String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
synchronized (mLock) {
return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
}
@@ -507,7 +514,7 @@
* 4. It needs to be added to the relevant user record.
*/
private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
- String callerPackageName, SessionCallbackLink cb, String tag) {
+ String callerPackageName, ISessionCallback cb, String tag) {
FullUserRecord user = getFullUserRecordLocked(userId);
if (user == null) {
Log.wtf(TAG, "Request from invalid user: " + userId);
@@ -517,7 +524,7 @@
final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
callerPackageName, cb, tag, this, mHandler.getLooper());
try {
- cb.getBinder().linkToDeath(session, 0);
+ cb.asBinder().linkToDeath(session, 0);
} catch (RemoteException e) {
throw new RuntimeException("Media Session owner died prematurely.", e);
}
@@ -868,7 +875,7 @@
private boolean mVoiceButtonHandled = false;
@Override
- public ISession createSession(String packageName, SessionCallbackLink cb, String tag,
+ public ISession createSession(String packageName, ISessionCallback cb, String tag,
int userId) throws RemoteException {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -1202,7 +1209,8 @@
* there's no active global priority session, long-pressess will be sent to the
* long-press listener instead of adjusting volume.
*
- * @param packageName The caller package.
+ * @param packageName The caller's package name, obtained by Context#getPackageName()
+ * @param opPackageName The caller's op package name, obtained by Context#getOpPackageName()
* @param asSystemService {@code true} if the event sent to the session as if it was come
* from the system service instead of the app process. This helps sessions to
* distinguish between the key injection by the app and key events from the
@@ -1217,8 +1225,8 @@
* @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
*/
@Override
- public void dispatchVolumeKeyEvent(String packageName, boolean asSystemService,
- KeyEvent keyEvent, int stream, boolean musicOnly) {
+ public void dispatchVolumeKeyEvent(String packageName, String opPackageName,
+ boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) {
if (keyEvent == null ||
(keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
&& keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
@@ -1240,8 +1248,8 @@
synchronized (mLock) {
if (isGlobalPriorityActiveLocked()
|| mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
- dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService,
- keyEvent, stream, musicOnly);
+ dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
+ asSystemService, keyEvent, stream, musicOnly);
} else {
// TODO: Consider the case when both volume up and down keys are pressed
// at the same time.
@@ -1274,12 +1282,13 @@
&& mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
.getDownTime() == keyEvent.getDownTime()) {
// Short-press. Should change volume.
- dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService,
+ dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
+ asSystemService,
mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
mCurrentFullUserRecord.mInitialDownVolumeStream,
mCurrentFullUserRecord.mInitialDownMusicOnly);
- dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService,
- keyEvent, stream, musicOnly);
+ dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
+ asSystemService, keyEvent, stream, musicOnly);
} else {
dispatchVolumeKeyLongPressLocked(keyEvent);
}
@@ -1291,8 +1300,9 @@
}
}
- private void dispatchVolumeKeyEventLocked(String packageName, int pid, int uid,
- boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) {
+ private void dispatchVolumeKeyEventLocked(String packageName, String opPackageName, int pid,
+ int uid, boolean asSystemService, KeyEvent keyEvent, int stream,
+ boolean musicOnly) {
boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
int direction = 0;
@@ -1326,26 +1336,26 @@
if (up) {
direction = 0;
}
- dispatchAdjustVolumeLocked(packageName, pid, uid, asSystemService, stream,
- direction, flags);
+ dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid,
+ asSystemService, stream, direction, flags);
} else if (isMute) {
if (down && keyEvent.getRepeatCount() == 0) {
- dispatchAdjustVolumeLocked(packageName, pid, uid, asSystemService, stream,
- AudioManager.ADJUST_TOGGLE_MUTE, flags);
+ dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid,
+ asSystemService, stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
}
}
}
}
@Override
- public void dispatchAdjustVolume(String packageName, int suggestedStream, int delta,
- int flags) {
+ public void dispatchAdjustVolume(String packageName, String opPackageName,
+ int suggestedStream, int delta, int flags) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- dispatchAdjustVolumeLocked(packageName, pid, uid, false,
+ dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, false,
suggestedStream, delta, flags);
}
} finally {
@@ -1409,24 +1419,14 @@
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- int controllerUserId = UserHandle.getUserId(controllerUid);
- int controllerUidFromPackageName;
- try {
- controllerUidFromPackageName = getContext().getPackageManager()
- .getPackageUidAsUser(controllerPackageName, controllerUserId);
- } catch (NameNotFoundException e) {
- if (DEBUG) {
- Log.d(TAG, "Package " + controllerPackageName + " doesn't exist");
- }
- return false;
- }
- if (controllerUidFromPackageName != controllerUid) {
- if (DEBUG) {
- Log.d(TAG, "Package name " + controllerPackageName
- + " doesn't match with the uid " + controllerUid);
- }
- return false;
- }
+ // Don't perform sanity check between controllerPackageName and controllerUid.
+ // When an (activity|service) runs on the another apps process by specifying
+ // android:process in the AndroidManifest.xml, then PID and UID would have the
+ // running process' information instead of the (activity|service) that has created
+ // MediaController.
+ // Note that we can use Context#getOpPackageName() instead of
+ // Context#getPackageName() for getting package name that matches with the PID/UID,
+ // but it doesn't tell which package has created the MediaController, so useless.
return hasMediaControlPermission(UserHandle.getUserId(uid), controllerPackageName,
controllerPid, controllerUid);
} finally {
@@ -1497,8 +1497,8 @@
return false;
}
- private void dispatchAdjustVolumeLocked(String packageName, int pid, int uid,
- boolean asSystemService, int suggestedStream, int direction, int flags) {
+ private void dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid,
+ int uid, boolean asSystemService, int suggestedStream, int direction, int flags) {
MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
: mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
@@ -1529,21 +1529,28 @@
mHandler.post(new Runnable() {
@Override
public void run() {
+ final String callingOpPackageName;
+ final int callingUid;
+ if (asSystemService) {
+ callingOpPackageName = getContext().getOpPackageName();
+ callingUid = Process.myUid();
+ } else {
+ callingOpPackageName = opPackageName;
+ callingUid = uid;
+ }
try {
- String packageName = getContext().getOpPackageName();
- mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
- flags, packageName, TAG);
- } catch (RemoteException|SecurityException e) {
- Log.e(TAG, "Error adjusting default volume.", e);
- } catch (IllegalArgumentException e) {
+ mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(suggestedStream,
+ direction, flags, callingOpPackageName, callingUid);
+ } catch (SecurityException | IllegalArgumentException e) {
Log.e(TAG, "Cannot adjust volume: direction=" + direction
- + ", suggestedStream=" + suggestedStream + ", flags=" + flags,
- e);
+ + ", suggestedStream=" + suggestedStream + ", flags=" + flags
+ + ", packageName=" + packageName + ", uid=" + uid
+ + ", asSystemService=" + asSystemService, e);
}
}
});
} else {
- session.adjustVolume(packageName, pid, uid, null, asSystemService,
+ session.adjustVolume(packageName, opPackageName, pid, uid, null, asSystemService,
direction, flags, true);
}
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 6187583..88d73fb 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -558,7 +558,7 @@
if (pkgList != null && (pkgList.length > 0)) {
boolean anyServicesInvolved = false;
// Remove notification settings for uninstalled package
- if (removingPackage) {
+ if (removingPackage && uidList != null) {
int size = Math.min(pkgList.length, uidList.length);
for (int i = 0; i < size; i++) {
final String pkg = pkgList[i];
@@ -570,9 +570,11 @@
if (mEnabledServicesPackageNames.contains(pkgName)) {
anyServicesInvolved = true;
}
- for (int uid : uidList) {
- if (isPackageAllowed(pkgName, UserHandle.getUserId(uid))) {
- anyServicesInvolved = true;
+ if (uidList != null && uidList.length > 0) {
+ for (int uid : uidList) {
+ if (isPackageAllowed(pkgName, UserHandle.getUserId(uid))) {
+ anyServicesInvolved = true;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e2cb75e..85a8d93 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5258,22 +5258,25 @@
}
if (DBG) Slog.v(TAG, "Interrupting!");
if (hasValidSound) {
- mSoundNotificationKey = key;
if (mInCall) {
playInCallNotification();
beep = true;
} else {
beep = playSound(record, soundUri);
}
+ if(beep) {
+ mSoundNotificationKey = key;
+ }
}
final boolean ringerModeSilent =
mAudioManager.getRingerModeInternal()
== AudioManager.RINGER_MODE_SILENT;
if (!mInCall && hasValidVibrate && !ringerModeSilent) {
- mVibrateNotificationKey = key;
-
buzz = playVibration(record, vibration, hasValidSound);
+ if(buzz) {
+ mVibrateNotificationKey = key;
+ }
}
} else if ((record.getFlags() & Notification.FLAG_INSISTENT) != 0) {
hasValidSound = false;
@@ -5454,8 +5457,17 @@
try {
Thread.sleep(waitMs);
} catch (InterruptedException e) { }
- mVibrator.vibrate(record.sbn.getUid(), record.sbn.getPackageName(),
- effect, "Notification (delayed)", record.getAudioAttributes());
+
+ // Notifications might be canceled before it actually vibrates due to waitMs,
+ // so need to check the notification still valide for vibrate.
+ synchronized (mNotificationLock) {
+ if (mNotificationsByKey.get(record.getKey()) != null) {
+ mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+ effect, "Notification (delayed)", record.getAudioAttributes());
+ } else {
+ Slog.e(TAG, "No vibration for canceled notification : " + record.getKey());
+ }
+ }
}).start();
} else {
mVibrator.vibrate(record.sbn.getUid(), record.sbn.getPackageName(),
diff --git a/services/core/java/com/android/server/pm/ModuleInfoProvider.java b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
index 886cfb2..642bfa2 100644
--- a/services/core/java/com/android/server/pm/ModuleInfoProvider.java
+++ b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
@@ -24,8 +24,8 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
-import android.os.Process;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
@@ -91,7 +91,7 @@
final PackageInfo pi;
try {
pi = mPackageManager.getPackageInfo(packageName,
- PackageManager.GET_META_DATA, Process.SYSTEM_UID);
+ PackageManager.GET_META_DATA, UserHandle.USER_SYSTEM);
Context packageContext = mContext.createPackageContext(packageName, 0);
packageResources = packageContext.getResources();
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 11b58fb..795f655 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -105,6 +105,7 @@
import com.android.server.LocalServices;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
+import com.android.server.pm.dex.DexManager;
import libcore.io.IoUtils;
@@ -1595,6 +1596,16 @@
}
}
}
+ if (baseApk.preferCodeIntegrity) {
+ for (File file : mResolvedStagedFiles) {
+ if (file.getName().endsWith(".apk")
+ && !DexManager.auditUncompressedCodeInApk(file.getPath())) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Some code are not uncompressed and aligned correctly for "
+ + mPackageName);
+ }
+ }
+ }
if (baseApk.isSplitRequired && stagedSplits.size() <= 1) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
"Missing split for " + mPackageName);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index da59b3e..136c7c9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -296,6 +296,7 @@
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
+import com.android.server.PackageWatchdog;
import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
import com.android.server.SystemServerInitThreadPool;
@@ -9492,6 +9493,7 @@
mPackageUsage.writeNow(mPackages);
mCompilerStats.writeNow();
mDexManager.writePackageDexUsageNow();
+ PackageWatchdog.getInstance(mContext).writeNow();
// This is the last chance to write out pending restriction settings
synchronized (mPackages) {
@@ -13021,20 +13023,26 @@
}
@Override
- public boolean canSuspendPackageForUser(String packageName, int userId) {
+ public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
- "canSuspendPackageForUser");
+ "getUnsuspendablePackagesForUser");
final int callingUid = Binder.getCallingUid();
if (UserHandle.getUserId(callingUid) != userId) {
throw new SecurityException("Calling uid " + callingUid
- + " cannot query canSuspendPackageForUser for user " + userId);
+ + " cannot query getUnsuspendablePackagesForUser for user " + userId);
}
+ final ArraySet<String> unactionablePackages = new ArraySet<>();
final long identity = Binder.clearCallingIdentity();
try {
- return canSuspendPackageForUserInternal(packageName, userId);
+ for (String packageName : packageNames) {
+ if (!canSuspendPackageForUserInternal(packageName, userId)) {
+ unactionablePackages.add(packageName);
+ }
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
+ return unactionablePackages.toArray(new String[unactionablePackages.size()]);
}
private boolean canSuspendPackageForUserInternal(String packageName, int userId) {
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 25ef767..e57d9d7 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -785,10 +785,10 @@
* files that can be direclty mapped.
*/
private static void logIfPackageHasUncompressedCode(PackageParser.Package pkg) {
- logIfApkHasUncompressedCode(pkg.baseCodePath);
+ auditUncompressedCodeInApk(pkg.baseCodePath);
if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
for (int i = 0; i < pkg.splitCodePaths.length; i++) {
- logIfApkHasUncompressedCode(pkg.splitCodePaths[i]);
+ auditUncompressedCodeInApk(pkg.splitCodePaths[i]);
}
}
}
@@ -797,34 +797,41 @@
* Generates log if the archive located at {@code fileName} has uncompressed dex file and so
* files that can be direclty mapped.
*/
- private static void logIfApkHasUncompressedCode(String fileName) {
+ public static boolean auditUncompressedCodeInApk(String fileName) {
StrictJarFile jarFile = null;
try {
jarFile = new StrictJarFile(fileName,
false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/);
Iterator<ZipEntry> it = jarFile.iterator();
+ boolean allCorrect = true;
while (it.hasNext()) {
ZipEntry entry = it.next();
if (entry.getName().endsWith(".dex")) {
if (entry.getMethod() != ZipEntry.STORED) {
+ allCorrect = false;
Slog.w(TAG, "APK " + fileName + " has compressed dex code " +
entry.getName());
} else if ((entry.getDataOffset() & 0x3) != 0) {
+ allCorrect = false;
Slog.w(TAG, "APK " + fileName + " has unaligned dex code " +
entry.getName());
}
} else if (entry.getName().endsWith(".so")) {
if (entry.getMethod() != ZipEntry.STORED) {
+ allCorrect = false;
Slog.w(TAG, "APK " + fileName + " has compressed native code " +
entry.getName());
} else if ((entry.getDataOffset() & (0x1000 - 1)) != 0) {
+ allCorrect = false;
Slog.w(TAG, "APK " + fileName + " has unaligned native code " +
entry.getName());
}
}
}
+ return allCorrect;
} catch (IOException ignore) {
Slog.wtf(TAG, "Error when parsing APK " + fileName);
+ return false;
} finally {
try {
if (jarFile != null) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f370edf..c792863 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -844,7 +844,7 @@
}
private void interceptBackKeyDown() {
- MetricsLogger.count(mContext, "key_back_down", 1);
+ mLogger.count("key_back_down", 1);
// Reset back key state for long press
mBackKeyHandled = false;
@@ -858,6 +858,7 @@
// returns true if the key was handled and should not be passed to the user
private boolean interceptBackKeyUp(KeyEvent event) {
+ mLogger.count("key_back_up", 1);
// Cache handled state
boolean handled = mBackKeyHandled;
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerService.java b/services/core/java/com/android/server/rollback/RollbackManagerService.java
index 5bf2040..8c3f04f 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerService.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerService.java
@@ -16,831 +16,37 @@
package com.android.server.rollback;
-import android.app.AppOpsManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
-import android.content.pm.ParceledListSlice;
-import android.content.pm.StringParceledListSlice;
-import android.content.rollback.IRollbackManager;
-import android.content.rollback.PackageRollbackInfo;
-import android.content.rollback.RollbackInfo;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
import android.os.SELinux;
import android.util.Log;
-import com.android.internal.annotations.GuardedBy;
-import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.pm.PackageManagerServiceUtils;
-
-import libcore.io.IoUtils;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.nio.file.Files;
-import java.time.Instant;
-import java.time.format.DateTimeParseException;
-import java.time.temporal.ChronoUnit;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
/**
- * Service that manages APK level rollbacks.
- *
- * TODO: Make RollbackManagerService extend SystemService and move the
- * IRollbackManager.Stub implementation to a new private
- * RollbackManagerServiceImpl class.
+ * Service that manages APK level rollbacks. Publishes
+ * Context.ROLLBACK_SERVICE.
*
* @hide
*/
-public class RollbackManagerService extends IRollbackManager.Stub {
+public final class RollbackManagerService extends SystemService {
private static final String TAG = "RollbackManager";
- // Rollbacks expire after 48 hours.
- // TODO: How to test rollback expiration works properly?
- private static final long ROLLBACK_LIFETIME_DURATION_MILLIS = 48 * 60 * 60 * 1000;
+ private RollbackManagerServiceImpl mService;
- // Lock used to synchronize accesses to in-memory rollback data
- // structures. By convention, methods with the suffix "Locked" require
- // mLock is held when they are called.
- private final Object mLock = new Object();
-
- // Package rollback data available to be used for rolling back a package.
- // This list is null until the rollback data has been loaded.
- @GuardedBy("mLock")
- private List<PackageRollbackData> mAvailableRollbacks;
-
- // The list of recently executed rollbacks.
- // This list is null until the rollback data has been loaded.
- @GuardedBy("mLock")
- private List<RollbackInfo> mRecentlyExecutedRollbacks;
-
- // Data for available rollbacks and recently executed rollbacks is
- // persisted in storage. Assuming the rollback data directory is
- // /data/rollback, we use the following directory structure
- // to store this data:
- // /data/rollback/
- // available/
- // com.package.A-XXX/
- // base.apk
- // rollback.json
- // com.package.B-YYY/
- // base.apk
- // rollback.json
- // recently_executed.json
- // TODO: Use AtomicFile for rollback.json and recently_executed.json.
- private final File mRollbackDataDir;
- private final File mAvailableRollbacksDir;
- private final File mRecentlyExecutedRollbacksFile;
-
- private final Context mContext;
- private final HandlerThread mHandlerThread;
-
- RollbackManagerService(Context context) {
- mContext = context;
- mHandlerThread = new HandlerThread("RollbackManagerServiceHandler");
- mHandlerThread.start();
-
- mRollbackDataDir = new File(Environment.getDataDirectory(), "rollback");
- mAvailableRollbacksDir = new File(mRollbackDataDir, "available");
- mRecentlyExecutedRollbacksFile = new File(mRollbackDataDir, "recently_executed.json");
-
- // Kick off loading of the rollback data from strorage in a background
- // thread.
- // TODO: Consider loading the rollback data directly here instead, to
- // avoid the need to call ensureRollbackDataLoaded every time before
- // accessing the rollback data?
- // TODO: Test that this kicks off initial scheduling of rollback
- // expiration.
- getHandler().post(() -> ensureRollbackDataLoaded());
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
- filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
- filter.addDataScheme("package");
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
- String packageName = intent.getData().getSchemeSpecificPart();
- onPackageReplaced(packageName);
- }
- if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
- String packageName = intent.getData().getSchemeSpecificPart();
- onPackageFullyRemoved(packageName);
- }
- }
- }, filter, null, getHandler());
-
- IntentFilter enableRollbackFilter = new IntentFilter();
- enableRollbackFilter.addAction(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
- try {
- enableRollbackFilter.addDataType("application/vnd.android.package-archive");
- } catch (IntentFilter.MalformedMimeTypeException e) {
- Log.e(TAG, "addDataType", e);
- }
-
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_PACKAGE_ENABLE_ROLLBACK.equals(intent.getAction())) {
- int token = intent.getIntExtra(
- PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
- int installFlags = intent.getIntExtra(
- PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS, 0);
- File newPackageCodePath = new File(intent.getData().getPath());
-
- getHandler().post(() -> {
- boolean success = enableRollback(installFlags, newPackageCodePath);
- int ret = PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED;
- if (!success) {
- ret = PackageManagerInternal.ENABLE_ROLLBACK_FAILED;
- }
-
- PackageManagerInternal pm = LocalServices.getService(
- PackageManagerInternal.class);
- pm.setEnableRollbackCode(token, ret);
- });
-
- // We're handling the ordered broadcast. Abort the
- // broadcast because there is no need for it to go to
- // anyone else.
- abortBroadcast();
- }
- }
- }, enableRollbackFilter, null, getHandler());
+ public RollbackManagerService(Context context) {
+ super(context);
}
@Override
- public RollbackInfo getAvailableRollback(String packageName) {
- PackageRollbackInfo.PackageVersion installedVersion =
- getInstalledPackageVersion(packageName);
- if (installedVersion == null) {
- return null;
- }
+ public void onStart() {
+ mService = new RollbackManagerServiceImpl(getContext());
- synchronized (mLock) {
- // TODO: Have ensureRollbackDataLoadedLocked return the list of
- // available rollbacks, to hopefully avoid forgetting to call it?
- ensureRollbackDataLoadedLocked();
- for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
- PackageRollbackData data = mAvailableRollbacks.get(i);
- if (data.info.higherVersion.equals(installedVersion)) {
- // TODO: For atomic installs, check all dependent packages
- // for available rollbacks and include that info here.
- return new RollbackInfo(data.info);
- }
- }
- }
-
- return null;
- }
-
- @Override
- public StringParceledListSlice getPackagesWithAvailableRollbacks() {
- // TODO: This may return packages whose rollback is out of date or
- // expired. Presumably that's okay because the package rollback could
- // be expired anyway between when the caller calls this method and
- // when the caller calls getAvailableRollback for more details.
- final Set<String> packageNames = new HashSet<>();
- synchronized (mLock) {
- ensureRollbackDataLoadedLocked();
- for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
- PackageRollbackData data = mAvailableRollbacks.get(i);
- packageNames.add(data.info.packageName);
- }
- }
- return new StringParceledListSlice(new ArrayList<>(packageNames));
- }
-
- @Override
- public ParceledListSlice<RollbackInfo> getRecentlyExecutedRollbacks() {
- synchronized (mLock) {
- ensureRollbackDataLoadedLocked();
- List<RollbackInfo> rollbacks = new ArrayList<>(mRecentlyExecutedRollbacks);
- return new ParceledListSlice<>(rollbacks);
- }
- }
-
- @Override
- public void executeRollback(RollbackInfo rollback, String callerPackageName,
- IntentSender statusReceiver) {
- final int callingUid = Binder.getCallingUid();
- if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
- AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
- appOps.checkPackage(callingUid, callerPackageName);
- }
-
- getHandler().post(() ->
- executeRollbackInternal(rollback, callerPackageName, statusReceiver));
- }
-
- /**
- * Performs the actual work to execute a rollback.
- * The work is done on the current thread. This may be a long running
- * operation.
- */
- private void executeRollbackInternal(RollbackInfo rollback,
- String callerPackageName, IntentSender statusReceiver) {
- String packageName = rollback.targetPackage.packageName;
- Log.i(TAG, "Initiating rollback of " + packageName);
-
- PackageRollbackInfo.PackageVersion installedVersion =
- getInstalledPackageVersion(packageName);
- if (installedVersion == null) {
- // TODO: Test this case
- sendFailure(statusReceiver, "Target package to roll back is not installed");
- return;
- }
-
- if (!rollback.targetPackage.higherVersion.equals(installedVersion)) {
- // TODO: Test this case
- sendFailure(statusReceiver, "Target package version to roll back not installed.");
- return;
- }
-
- // 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.
- PackageRollbackData data = null;
- synchronized (mLock) {
- ensureRollbackDataLoadedLocked();
- for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
- PackageRollbackData available = mAvailableRollbacks.get(i);
- // TODO: Check if available.info.lowerVersion matches
- // rollback.targetPackage.lowerVersion?
- if (available.info.higherVersion.equals(installedVersion)) {
- data = available;
- break;
- }
- }
- }
-
- if (data == null) {
- sendFailure(statusReceiver, "Rollback not available");
- return;
- }
-
- // Get a context for the caller to use to install the downgraded
- // version of the package.
- Context context = null;
- try {
- context = mContext.createPackageContext(callerPackageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- sendFailure(statusReceiver, "Invalid callerPackageName");
- return;
- }
-
- PackageManager pm = context.getPackageManager();
- try {
- PackageInstaller.Session session = null;
-
- PackageInstaller packageInstaller = pm.getPackageInstaller();
- PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
- PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- params.setAllowDowngrade(true);
- int sessionId = packageInstaller.createSession(params);
- session = packageInstaller.openSession(sessionId);
-
- // TODO: Will it always be called "base.apk"? What about splits?
- File baseApk = new File(data.backupDir, "base.apk");
- try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(baseApk,
- ParcelFileDescriptor.MODE_READ_ONLY)) {
- final long token = Binder.clearCallingIdentity();
- try {
- session.write("base.apk", 0, baseApk.length(), fd);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- final LocalIntentReceiver receiver = new LocalIntentReceiver();
- session.commit(receiver.getIntentSender());
-
- Intent result = receiver.getResult();
- int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status != PackageInstaller.STATUS_SUCCESS) {
- sendFailure(statusReceiver, "Rollback downgrade install failed: "
- + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE));
- return;
- }
-
- addRecentlyExecutedRollback(rollback);
- sendSuccess(statusReceiver);
-
- // TODO: Restrict permissions for who can listen for this
- // broadcast?
- Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED,
- Uri.fromParts("package", packageName, null));
-
- // TODO: This call emits the warning "Calling a method in the
- // system process without a qualified user". Fix that.
- mContext.sendBroadcast(broadcast);
- } catch (IOException e) {
- Log.e(TAG, "Unable to roll back " + packageName, e);
- sendFailure(statusReceiver, "IOException: " + e.toString());
- return;
- }
- }
-
- @Override
- public void reloadPersistedData() {
- synchronized (mLock) {
- mAvailableRollbacks = null;
- mRecentlyExecutedRollbacks = null;
- }
- getHandler().post(() -> ensureRollbackDataLoaded());
- }
-
- @Override
- public void expireRollbackForPackage(String packageName) {
- // TODO: Should this take a package version number in addition to
- // package name? For now, just remove all rollbacks matching the
- // package name. This method is only currently used to facilitate
- // testing anyway.
- synchronized (mLock) {
- ensureRollbackDataLoadedLocked();
- Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
- while (iter.hasNext()) {
- PackageRollbackData data = iter.next();
- if (data.info.packageName.equals(packageName)) {
- iter.remove();
- removeFile(data.backupDir);
- }
- }
- }
- }
-
- /**
- * Load rollback data from storage if it has not already been loaded.
- * After calling this funciton, mAvailableRollbacks and
- * mRecentlyExecutedRollbacks will be non-null.
- */
- private void ensureRollbackDataLoaded() {
- synchronized (mLock) {
- ensureRollbackDataLoadedLocked();
- }
- }
-
- /**
- * Load rollback data from storage if it has not already been loaded.
- * After calling this function, mAvailableRollbacks and
- * mRecentlyExecutedRollbacks will be non-null.
- */
- @GuardedBy("mLock")
- private void ensureRollbackDataLoadedLocked() {
- if (mAvailableRollbacks == null) {
- loadRollbackDataLocked();
- }
- }
-
- /**
- * Load rollback data from storage.
- * Note: We do potentially heavy IO here while holding mLock, because we
- * have to have the rollback data loaded before we can do anything else
- * meaningful.
- */
- @GuardedBy("mLock")
- private void loadRollbackDataLocked() {
- mAvailableRollbacksDir.mkdirs();
- mAvailableRollbacks = new ArrayList<>();
- for (File rollbackDir : mAvailableRollbacksDir.listFiles()) {
- if (rollbackDir.isDirectory()) {
- // TODO: How to detect and clean up an invalid rollback
- // directory? We don't know if it's invalid because something
- // went wrong, or if it's only temporarily invalid because
- // it's in the process of being created.
- try {
- File jsonFile = new File(rollbackDir, "rollback.json");
- String jsonString = IoUtils.readFileAsString(jsonFile.getAbsolutePath());
- JSONObject jsonObject = new JSONObject(jsonString);
- String packageName = jsonObject.getString("packageName");
- long higherVersionCode = jsonObject.getLong("higherVersionCode");
- long lowerVersionCode = jsonObject.getLong("lowerVersionCode");
- Instant timestamp = Instant.parse(jsonObject.getString("timestamp"));
- PackageRollbackData data = new PackageRollbackData(
- new PackageRollbackInfo(packageName,
- new PackageRollbackInfo.PackageVersion(higherVersionCode),
- new PackageRollbackInfo.PackageVersion(lowerVersionCode)),
- rollbackDir, timestamp);
- mAvailableRollbacks.add(data);
- } catch (IOException | JSONException | DateTimeParseException e) {
- Log.e(TAG, "Unable to read rollback data at " + rollbackDir, e);
- }
- }
- }
-
- mRecentlyExecutedRollbacks = new ArrayList<>();
- if (mRecentlyExecutedRollbacksFile.exists()) {
- try {
- // TODO: How to cope with changes to the format of this file from
- // when RollbackStore is updated in the future?
- String jsonString = IoUtils.readFileAsString(
- mRecentlyExecutedRollbacksFile.getAbsolutePath());
- JSONObject object = new JSONObject(jsonString);
- JSONArray array = object.getJSONArray("recentlyExecuted");
- for (int i = 0; i < array.length(); ++i) {
- JSONObject element = array.getJSONObject(i);
- String packageName = element.getString("packageName");
- long higherVersionCode = element.getLong("higherVersionCode");
- long lowerVersionCode = element.getLong("lowerVersionCode");
- PackageRollbackInfo target = new PackageRollbackInfo(packageName,
- new PackageRollbackInfo.PackageVersion(higherVersionCode),
- new PackageRollbackInfo.PackageVersion(lowerVersionCode));
- RollbackInfo rollback = new RollbackInfo(target);
- mRecentlyExecutedRollbacks.add(rollback);
- }
- } catch (IOException | JSONException e) {
- // TODO: What to do here? Surely we shouldn't just forget about
- // everything after the point of exception?
- Log.e(TAG, "Failed to read recently executed rollbacks", e);
- }
- }
-
- scheduleExpiration(0);
- }
-
- /**
- * Called when a package has been replaced with a different version.
- * Removes all backups for the package not matching the currently
- * installed package version.
- */
- private void onPackageReplaced(String packageName) {
- // TODO: Could this end up incorrectly deleting a rollback for a
- // package that is about to be installed?
- PackageRollbackInfo.PackageVersion installedVersion =
- getInstalledPackageVersion(packageName);
-
- synchronized (mLock) {
- ensureRollbackDataLoadedLocked();
- Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
- while (iter.hasNext()) {
- PackageRollbackData data = iter.next();
- if (data.info.packageName.equals(packageName)
- && !data.info.higherVersion.equals(installedVersion)) {
- iter.remove();
- removeFile(data.backupDir);
- }
- }
- }
- }
-
- /**
- * Called when a package has been completely removed from the device.
- * Removes all backups and rollback history for the given package.
- */
- private void onPackageFullyRemoved(String packageName) {
- expireRollbackForPackage(packageName);
-
- synchronized (mLock) {
- ensureRollbackDataLoadedLocked();
- Iterator<RollbackInfo> iter = mRecentlyExecutedRollbacks.iterator();
- boolean changed = false;
- while (iter.hasNext()) {
- RollbackInfo rollback = iter.next();
- if (packageName.equals(rollback.targetPackage.packageName)) {
- iter.remove();
- changed = true;
- }
- }
-
- if (changed) {
- saveRecentlyExecutedRollbacksLocked();
- }
- }
- }
-
- /**
- * Write the list of recently executed rollbacks to storage.
- * Note: This happens while mLock is held, which should be okay because we
- * expect executed rollbacks to be modified only in exceptional cases.
- */
- @GuardedBy("mLock")
- private void saveRecentlyExecutedRollbacksLocked() {
- try {
- JSONObject json = new JSONObject();
- JSONArray array = new JSONArray();
- json.put("recentlyExecuted", array);
-
- for (int i = 0; i < mRecentlyExecutedRollbacks.size(); ++i) {
- RollbackInfo rollback = mRecentlyExecutedRollbacks.get(i);
- JSONObject element = new JSONObject();
- element.put("packageName", rollback.targetPackage.packageName);
- element.put("higherVersionCode", rollback.targetPackage.higherVersion.versionCode);
- element.put("lowerVersionCode", rollback.targetPackage.lowerVersion.versionCode);
- array.put(element);
- }
-
- PrintWriter pw = new PrintWriter(mRecentlyExecutedRollbacksFile);
- pw.println(json.toString());
- pw.close();
- } catch (IOException | JSONException e) {
- // TODO: What to do here?
- Log.e(TAG, "Failed to save recently executed rollbacks", e);
- }
- }
-
- /**
- * Records that the given package has been recently rolled back.
- */
- private void addRecentlyExecutedRollback(RollbackInfo rollback) {
- // TODO: if the list of rollbacks gets too big, trim it to only those
- // that are necessary to keep track of.
- synchronized (mLock) {
- ensureRollbackDataLoadedLocked();
- mRecentlyExecutedRollbacks.add(rollback);
- saveRecentlyExecutedRollbacksLocked();
- }
- }
-
- /**
- * Notifies an IntentSender of failure.
- *
- * @param statusReceiver where to send the failure
- * @param message the failure message.
- */
- private void sendFailure(IntentSender statusReceiver, String message) {
- Log.e(TAG, message);
- try {
- // TODO: More context on which rollback failed?
- // TODO: More refined failure code?
- final Intent fillIn = new Intent();
- fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
- fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, message);
- statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
- } catch (IntentSender.SendIntentException e) {
- // Nowhere to send the result back to, so don't bother.
- }
- }
-
- /**
- * Notifies an IntentSender of success.
- */
- private void sendSuccess(IntentSender statusReceiver) {
- try {
- final Intent fillIn = new Intent();
- fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_SUCCESS);
- statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
- } catch (IntentSender.SendIntentException e) {
- // Nowhere to send the result back to, so don't bother.
- }
- }
-
- // Check to see if anything needs expiration, and if so, expire it.
- // Schedules future expiration as appropriate.
- // TODO: Handle cases where the user changes time on the device.
- private void runExpiration() {
- Instant now = Instant.now();
- Instant oldest = null;
- synchronized (mLock) {
- ensureRollbackDataLoadedLocked();
-
- Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
- while (iter.hasNext()) {
- PackageRollbackData data = iter.next();
- if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) {
- iter.remove();
- removeFile(data.backupDir);
- } else if (oldest == null || oldest.isAfter(data.timestamp)) {
- oldest = data.timestamp;
- }
- }
- }
-
- if (oldest != null) {
- scheduleExpiration(now.until(oldest.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS),
- ChronoUnit.MILLIS));
- }
- }
-
- /**
- * Schedules an expiration check to be run after the given duration in
- * milliseconds has gone by.
- */
- private void scheduleExpiration(long duration) {
- getHandler().postDelayed(() -> runExpiration(), duration);
- }
-
- /**
- * Gets the RollbackManagerService's handler.
- * To allow PackageManagerService to call into RollbackManagerService
- * without fear of blocking the PackageManagerService thread.
- */
- public Handler getHandler() {
- return mHandlerThread.getThreadHandler();
- }
-
- /**
- * Called via broadcast by the package manager when a package is being
- * staged for install with rollback enabled. Called before the package has
- * been installed.
- *
- * @param id the id of the enable rollback request
- * @param installFlags information about what is being installed.
- * @param newPackageCodePath path to the package about to be installed.
- * @return true if enabling the rollback succeeds, false otherwise.
- */
- private boolean enableRollback(int installFlags, File newPackageCodePath) {
- if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
- Log.e(TAG, "Rollbacks not supported for instant app install");
- return false;
- }
- if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
- Log.e(TAG, "Rollbacks not supported for apex install");
- return false;
- }
-
- // Get information about the package to be installed.
- PackageParser.PackageLite newPackage = null;
- try {
- newPackage = PackageParser.parsePackageLite(newPackageCodePath, 0);
- } catch (PackageParser.PackageParserException e) {
- Log.e(TAG, "Unable to parse new package", e);
- return false;
- }
-
- String packageName = newPackage.packageName;
- Log.i(TAG, "Enabling rollback for install of " + packageName);
-
- PackageRollbackInfo.PackageVersion newVersion =
- new PackageRollbackInfo.PackageVersion(newPackage.versionCode);
-
- // Get information about the currently installed package.
- PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
- PackageParser.Package installedPackage = pm.getPackage(packageName);
- if (installedPackage == null) {
- // TODO: Support rolling back fresh package installs rather than
- // fail here. Test this case.
- Log.e(TAG, packageName + " is not installed");
- return false;
- }
- PackageRollbackInfo.PackageVersion installedVersion =
- new PackageRollbackInfo.PackageVersion(installedPackage.getLongVersionCode());
-
- File backupDir;
- try {
- backupDir = Files.createTempDirectory(
- mAvailableRollbacksDir.toPath(), packageName + "-").toFile();
- } catch (IOException e) {
- Log.e(TAG, "Unable to create rollback for " + packageName, e);
- return false;
- }
-
- // TODO: Should the timestamp be for when we commit the install, not
- // when we create the pending one?
- Instant timestamp = Instant.now();
- try {
- JSONObject json = new JSONObject();
- json.put("packageName", packageName);
- json.put("higherVersionCode", newVersion.versionCode);
- json.put("lowerVersionCode", installedVersion.versionCode);
- json.put("timestamp", timestamp.toString());
-
- File jsonFile = new File(backupDir, "rollback.json");
- PrintWriter pw = new PrintWriter(jsonFile);
- pw.println(json.toString());
- pw.close();
- } catch (IOException | JSONException e) {
- Log.e(TAG, "Unable to create rollback for " + packageName, e);
- removeFile(backupDir);
- return false;
- }
-
- // TODO: Copy by hard link instead to save on cpu and storage space?
- int status = PackageManagerServiceUtils.copyPackage(installedPackage.codePath, backupDir);
- if (status != PackageManager.INSTALL_SUCCEEDED) {
- Log.e(TAG, "Unable to copy package for rollback for " + packageName);
- removeFile(backupDir);
- return false;
- }
-
- PackageRollbackData data = new PackageRollbackData(
- new PackageRollbackInfo(packageName, newVersion, installedVersion),
- backupDir, timestamp);
-
- synchronized (mLock) {
- ensureRollbackDataLoadedLocked();
- mAvailableRollbacks.add(data);
- }
-
- return true;
- }
-
- // TODO: Don't copy this from PackageManagerShellCommand like this?
- private static class LocalIntentReceiver {
- private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
-
- private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
- @Override
- public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
- IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
- try {
- mResult.offer(intent, 5, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- };
-
- public IntentSender getIntentSender() {
- return new IntentSender((IIntentSender) mLocalSender);
- }
-
- public Intent getResult() {
- try {
- return mResult.take();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- /**
- * Deletes a file completely.
- * If the file is a directory, its contents are deleted as well.
- * Has no effect if the directory does not exist.
- */
- private void removeFile(File file) {
- if (file.isDirectory()) {
- for (File child : file.listFiles()) {
- removeFile(child);
- }
- }
- if (file.exists()) {
- file.delete();
- }
- }
-
- /**
- * Gets the version of the package currently installed.
- * Returns null if the package is not currently installed.
- */
- private PackageRollbackInfo.PackageVersion getInstalledPackageVersion(String packageName) {
- PackageManager pm = mContext.getPackageManager();
- PackageInfo pkgInfo = null;
- try {
- pkgInfo = pm.getPackageInfo(packageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- return null;
- }
-
- return new PackageRollbackInfo.PackageVersion(pkgInfo.getLongVersionCode());
- }
-
- /**
- * Manages the lifecycle of RollbackManagerService within System Server.
- */
- public static class Lifecycle extends SystemService {
- private RollbackManagerService mService;
-
- public Lifecycle(Context context) {
- super(context);
- }
-
- @Override
- public void onStart() {
- mService = new RollbackManagerService(getContext());
-
- // TODO: Set up sepolicy to allow publishing the service.
- if (SELinux.isSELinuxEnforced()) {
- Log.w(TAG, "RollbackManager disabled pending selinux policy updates");
- } else {
- publishBinderService(Context.ROLLBACK_SERVICE, mService);
- }
+ // TODO: Set up sepolicy to allow publishing the service.
+ if (SELinux.isSELinuxEnforced()) {
+ Log.w(TAG, "RollbackManager disabled pending selinux policy updates");
+ } else {
+ publishBinderService(Context.ROLLBACK_SERVICE, mService);
}
}
}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
new file mode 100644
index 0000000..0c21312
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -0,0 +1,832 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.rollback;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.StringParceledListSlice;
+import android.content.rollback.IRollbackManager;
+import android.content.rollback.PackageRollbackInfo;
+import android.content.rollback.RollbackInfo;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+import com.android.server.pm.PackageManagerServiceUtils;
+
+import libcore.io.IoUtils;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.time.Instant;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Implementation of service that manages APK level rollbacks.
+ */
+class RollbackManagerServiceImpl extends IRollbackManager.Stub {
+
+ private static final String TAG = "RollbackManager";
+
+ // Rollbacks expire after 48 hours.
+ // TODO: How to test rollback expiration works properly?
+ private static final long ROLLBACK_LIFETIME_DURATION_MILLIS = 48 * 60 * 60 * 1000;
+
+ // Lock used to synchronize accesses to in-memory rollback data
+ // structures. By convention, methods with the suffix "Locked" require
+ // mLock is held when they are called.
+ private final Object mLock = new Object();
+
+ // Package rollback data available to be used for rolling back a package.
+ // This list is null until the rollback data has been loaded.
+ @GuardedBy("mLock")
+ private List<PackageRollbackData> mAvailableRollbacks;
+
+ // The list of recently executed rollbacks.
+ // This list is null until the rollback data has been loaded.
+ @GuardedBy("mLock")
+ private List<RollbackInfo> mRecentlyExecutedRollbacks;
+
+ // Data for available rollbacks and recently executed rollbacks is
+ // persisted in storage. Assuming the rollback data directory is
+ // /data/rollback, we use the following directory structure
+ // to store this data:
+ // /data/rollback/
+ // available/
+ // com.package.A-XXX/
+ // base.apk
+ // rollback.json
+ // com.package.B-YYY/
+ // base.apk
+ // rollback.json
+ // recently_executed.json
+ // TODO: Use AtomicFile for rollback.json and recently_executed.json.
+ private final File mRollbackDataDir;
+ private final File mAvailableRollbacksDir;
+ private final File mRecentlyExecutedRollbacksFile;
+
+ private final Context mContext;
+ private final HandlerThread mHandlerThread;
+
+ RollbackManagerServiceImpl(Context context) {
+ mContext = context;
+ mHandlerThread = new HandlerThread("RollbackManagerServiceHandler");
+ mHandlerThread.start();
+
+ mRollbackDataDir = new File(Environment.getDataDirectory(), "rollback");
+ mAvailableRollbacksDir = new File(mRollbackDataDir, "available");
+ mRecentlyExecutedRollbacksFile = new File(mRollbackDataDir, "recently_executed.json");
+
+ // Kick off loading of the rollback data from strorage in a background
+ // thread.
+ // TODO: Consider loading the rollback data directly here instead, to
+ // avoid the need to call ensureRollbackDataLoaded every time before
+ // accessing the rollback data?
+ // TODO: Test that this kicks off initial scheduling of rollback
+ // expiration.
+ getHandler().post(() -> ensureRollbackDataLoaded());
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+ String packageName = intent.getData().getSchemeSpecificPart();
+ onPackageReplaced(packageName);
+ }
+ if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
+ String packageName = intent.getData().getSchemeSpecificPart();
+ onPackageFullyRemoved(packageName);
+ }
+ }
+ }, filter, null, getHandler());
+
+ IntentFilter enableRollbackFilter = new IntentFilter();
+ enableRollbackFilter.addAction(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
+ try {
+ enableRollbackFilter.addDataType("application/vnd.android.package-archive");
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ Log.e(TAG, "addDataType", e);
+ }
+
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_PACKAGE_ENABLE_ROLLBACK.equals(intent.getAction())) {
+ int token = intent.getIntExtra(
+ PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
+ int installFlags = intent.getIntExtra(
+ PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS, 0);
+ File newPackageCodePath = new File(intent.getData().getPath());
+
+ getHandler().post(() -> {
+ boolean success = enableRollback(installFlags, newPackageCodePath);
+ int ret = PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED;
+ if (!success) {
+ ret = PackageManagerInternal.ENABLE_ROLLBACK_FAILED;
+ }
+
+ PackageManagerInternal pm = LocalServices.getService(
+ PackageManagerInternal.class);
+ pm.setEnableRollbackCode(token, ret);
+ });
+
+ // We're handling the ordered broadcast. Abort the
+ // broadcast because there is no need for it to go to
+ // anyone else.
+ abortBroadcast();
+ }
+ }
+ }, enableRollbackFilter, null, getHandler());
+ }
+
+ @Override
+ public RollbackInfo getAvailableRollback(String packageName) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_ROLLBACKS,
+ "getAvailableRollback");
+
+ PackageRollbackInfo.PackageVersion installedVersion =
+ getInstalledPackageVersion(packageName);
+ if (installedVersion == null) {
+ return null;
+ }
+
+ synchronized (mLock) {
+ // TODO: Have ensureRollbackDataLoadedLocked return the list of
+ // available rollbacks, to hopefully avoid forgetting to call it?
+ ensureRollbackDataLoadedLocked();
+ for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
+ PackageRollbackData data = mAvailableRollbacks.get(i);
+ if (data.info.packageName.equals(packageName)
+ && data.info.higherVersion.equals(installedVersion)) {
+ // TODO: For atomic installs, check all dependent packages
+ // for available rollbacks and include that info here.
+ return new RollbackInfo(data.info);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public StringParceledListSlice getPackagesWithAvailableRollbacks() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_ROLLBACKS,
+ "getPackagesWithAvailableRollbacks");
+
+ // TODO: This may return packages whose rollback is out of date or
+ // expired. Presumably that's okay because the package rollback could
+ // be expired anyway between when the caller calls this method and
+ // when the caller calls getAvailableRollback for more details.
+ final Set<String> packageNames = new HashSet<>();
+ synchronized (mLock) {
+ ensureRollbackDataLoadedLocked();
+ for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
+ PackageRollbackData data = mAvailableRollbacks.get(i);
+ packageNames.add(data.info.packageName);
+ }
+ }
+ return new StringParceledListSlice(new ArrayList<>(packageNames));
+ }
+
+ @Override
+ public ParceledListSlice<RollbackInfo> getRecentlyExecutedRollbacks() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_ROLLBACKS,
+ "getRecentlyExecutedRollbacks");
+
+ synchronized (mLock) {
+ ensureRollbackDataLoadedLocked();
+ List<RollbackInfo> rollbacks = new ArrayList<>(mRecentlyExecutedRollbacks);
+ return new ParceledListSlice<>(rollbacks);
+ }
+ }
+
+ @Override
+ public void executeRollback(RollbackInfo rollback, String callerPackageName,
+ IntentSender statusReceiver) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_ROLLBACKS,
+ "executeRollback");
+
+ final int callingUid = Binder.getCallingUid();
+ AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+ appOps.checkPackage(callingUid, callerPackageName);
+
+ getHandler().post(() ->
+ executeRollbackInternal(rollback, callerPackageName, statusReceiver));
+ }
+
+ /**
+ * Performs the actual work to execute a rollback.
+ * The work is done on the current thread. This may be a long running
+ * operation.
+ */
+ private void executeRollbackInternal(RollbackInfo rollback,
+ String callerPackageName, IntentSender statusReceiver) {
+ String packageName = rollback.targetPackage.packageName;
+ Log.i(TAG, "Initiating rollback of " + packageName);
+
+ PackageRollbackInfo.PackageVersion installedVersion =
+ getInstalledPackageVersion(packageName);
+ if (installedVersion == null) {
+ // TODO: Test this case
+ sendFailure(statusReceiver, "Target package to roll back is not installed");
+ return;
+ }
+
+ if (!rollback.targetPackage.higherVersion.equals(installedVersion)) {
+ // TODO: Test this case
+ sendFailure(statusReceiver, "Target package version to roll back not installed.");
+ return;
+ }
+
+ // 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.
+ PackageRollbackData data = null;
+ synchronized (mLock) {
+ ensureRollbackDataLoadedLocked();
+ for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
+ PackageRollbackData available = mAvailableRollbacks.get(i);
+ // TODO: Check if available.info.lowerVersion matches
+ // rollback.targetPackage.lowerVersion?
+ if (available.info.packageName.equals(packageName)
+ && available.info.higherVersion.equals(installedVersion)) {
+ data = available;
+ break;
+ }
+ }
+ }
+
+ if (data == null) {
+ sendFailure(statusReceiver, "Rollback not available");
+ return;
+ }
+
+ // Get a context for the caller to use to install the downgraded
+ // version of the package.
+ Context context = null;
+ try {
+ context = mContext.createPackageContext(callerPackageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ sendFailure(statusReceiver, "Invalid callerPackageName");
+ return;
+ }
+
+ PackageManager pm = context.getPackageManager();
+ try {
+ PackageInstaller.Session session = null;
+
+ PackageInstaller packageInstaller = pm.getPackageInstaller();
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ params.setAllowDowngrade(true);
+ int sessionId = packageInstaller.createSession(params);
+ session = packageInstaller.openSession(sessionId);
+
+ // TODO: Will it always be called "base.apk"? What about splits?
+ File baseApk = new File(data.backupDir, "base.apk");
+ try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(baseApk,
+ ParcelFileDescriptor.MODE_READ_ONLY)) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ session.write("base.apk", 0, baseApk.length(), fd);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ final LocalIntentReceiver receiver = new LocalIntentReceiver();
+ session.commit(receiver.getIntentSender());
+
+ Intent result = receiver.getResult();
+ int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status != PackageInstaller.STATUS_SUCCESS) {
+ sendFailure(statusReceiver, "Rollback downgrade install failed: "
+ + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE));
+ return;
+ }
+
+ addRecentlyExecutedRollback(rollback);
+ sendSuccess(statusReceiver);
+
+ Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED,
+ Uri.fromParts("package", packageName, Manifest.permission.MANAGE_ROLLBACKS));
+
+ // TODO: This call emits the warning "Calling a method in the
+ // system process without a qualified user". Fix that.
+ mContext.sendBroadcast(broadcast);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to roll back " + packageName, e);
+ sendFailure(statusReceiver, "IOException: " + e.toString());
+ return;
+ }
+ }
+
+ @Override
+ public void reloadPersistedData() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_ROLLBACKS,
+ "reloadPersistedData");
+
+ synchronized (mLock) {
+ mAvailableRollbacks = null;
+ mRecentlyExecutedRollbacks = null;
+ }
+ getHandler().post(() -> ensureRollbackDataLoaded());
+ }
+
+ @Override
+ public void expireRollbackForPackage(String packageName) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_ROLLBACKS,
+ "expireRollbackForPackage");
+
+ // TODO: Should this take a package version number in addition to
+ // package name? For now, just remove all rollbacks matching the
+ // package name. This method is only currently used to facilitate
+ // testing anyway.
+ synchronized (mLock) {
+ ensureRollbackDataLoadedLocked();
+ Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
+ while (iter.hasNext()) {
+ PackageRollbackData data = iter.next();
+ if (data.info.packageName.equals(packageName)) {
+ iter.remove();
+ removeFile(data.backupDir);
+ }
+ }
+ }
+ }
+
+ /**
+ * Load rollback data from storage if it has not already been loaded.
+ * After calling this funciton, mAvailableRollbacks and
+ * mRecentlyExecutedRollbacks will be non-null.
+ */
+ private void ensureRollbackDataLoaded() {
+ synchronized (mLock) {
+ ensureRollbackDataLoadedLocked();
+ }
+ }
+
+ /**
+ * Load rollback data from storage if it has not already been loaded.
+ * After calling this function, mAvailableRollbacks and
+ * mRecentlyExecutedRollbacks will be non-null.
+ */
+ @GuardedBy("mLock")
+ private void ensureRollbackDataLoadedLocked() {
+ if (mAvailableRollbacks == null) {
+ loadRollbackDataLocked();
+ }
+ }
+
+ /**
+ * Load rollback data from storage.
+ * Note: We do potentially heavy IO here while holding mLock, because we
+ * have to have the rollback data loaded before we can do anything else
+ * meaningful.
+ */
+ @GuardedBy("mLock")
+ private void loadRollbackDataLocked() {
+ mAvailableRollbacksDir.mkdirs();
+ mAvailableRollbacks = new ArrayList<>();
+ for (File rollbackDir : mAvailableRollbacksDir.listFiles()) {
+ if (rollbackDir.isDirectory()) {
+ // TODO: How to detect and clean up an invalid rollback
+ // directory? We don't know if it's invalid because something
+ // went wrong, or if it's only temporarily invalid because
+ // it's in the process of being created.
+ try {
+ File jsonFile = new File(rollbackDir, "rollback.json");
+ String jsonString = IoUtils.readFileAsString(jsonFile.getAbsolutePath());
+ JSONObject jsonObject = new JSONObject(jsonString);
+ String packageName = jsonObject.getString("packageName");
+ long higherVersionCode = jsonObject.getLong("higherVersionCode");
+ long lowerVersionCode = jsonObject.getLong("lowerVersionCode");
+ Instant timestamp = Instant.parse(jsonObject.getString("timestamp"));
+ PackageRollbackData data = new PackageRollbackData(
+ new PackageRollbackInfo(packageName,
+ new PackageRollbackInfo.PackageVersion(higherVersionCode),
+ new PackageRollbackInfo.PackageVersion(lowerVersionCode)),
+ rollbackDir, timestamp);
+ mAvailableRollbacks.add(data);
+ } catch (IOException | JSONException | DateTimeParseException e) {
+ Log.e(TAG, "Unable to read rollback data at " + rollbackDir, e);
+ }
+ }
+ }
+
+ mRecentlyExecutedRollbacks = new ArrayList<>();
+ if (mRecentlyExecutedRollbacksFile.exists()) {
+ try {
+ // TODO: How to cope with changes to the format of this file from
+ // when RollbackStore is updated in the future?
+ String jsonString = IoUtils.readFileAsString(
+ mRecentlyExecutedRollbacksFile.getAbsolutePath());
+ JSONObject object = new JSONObject(jsonString);
+ JSONArray array = object.getJSONArray("recentlyExecuted");
+ for (int i = 0; i < array.length(); ++i) {
+ JSONObject element = array.getJSONObject(i);
+ String packageName = element.getString("packageName");
+ long higherVersionCode = element.getLong("higherVersionCode");
+ long lowerVersionCode = element.getLong("lowerVersionCode");
+ PackageRollbackInfo target = new PackageRollbackInfo(packageName,
+ new PackageRollbackInfo.PackageVersion(higherVersionCode),
+ new PackageRollbackInfo.PackageVersion(lowerVersionCode));
+ RollbackInfo rollback = new RollbackInfo(target);
+ mRecentlyExecutedRollbacks.add(rollback);
+ }
+ } catch (IOException | JSONException e) {
+ // TODO: What to do here? Surely we shouldn't just forget about
+ // everything after the point of exception?
+ Log.e(TAG, "Failed to read recently executed rollbacks", e);
+ }
+ }
+
+ scheduleExpiration(0);
+ }
+
+ /**
+ * Called when a package has been replaced with a different version.
+ * Removes all backups for the package not matching the currently
+ * installed package version.
+ */
+ private void onPackageReplaced(String packageName) {
+ // TODO: Could this end up incorrectly deleting a rollback for a
+ // package that is about to be installed?
+ PackageRollbackInfo.PackageVersion installedVersion =
+ getInstalledPackageVersion(packageName);
+
+ synchronized (mLock) {
+ ensureRollbackDataLoadedLocked();
+ Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
+ while (iter.hasNext()) {
+ PackageRollbackData data = iter.next();
+ if (data.info.packageName.equals(packageName)
+ && !data.info.higherVersion.equals(installedVersion)) {
+ iter.remove();
+ removeFile(data.backupDir);
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when a package has been completely removed from the device.
+ * Removes all backups and rollback history for the given package.
+ */
+ private void onPackageFullyRemoved(String packageName) {
+ expireRollbackForPackage(packageName);
+
+ synchronized (mLock) {
+ ensureRollbackDataLoadedLocked();
+ Iterator<RollbackInfo> iter = mRecentlyExecutedRollbacks.iterator();
+ boolean changed = false;
+ while (iter.hasNext()) {
+ RollbackInfo rollback = iter.next();
+ if (packageName.equals(rollback.targetPackage.packageName)) {
+ iter.remove();
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ saveRecentlyExecutedRollbacksLocked();
+ }
+ }
+ }
+
+ /**
+ * Write the list of recently executed rollbacks to storage.
+ * Note: This happens while mLock is held, which should be okay because we
+ * expect executed rollbacks to be modified only in exceptional cases.
+ */
+ @GuardedBy("mLock")
+ private void saveRecentlyExecutedRollbacksLocked() {
+ try {
+ JSONObject json = new JSONObject();
+ JSONArray array = new JSONArray();
+ json.put("recentlyExecuted", array);
+
+ for (int i = 0; i < mRecentlyExecutedRollbacks.size(); ++i) {
+ RollbackInfo rollback = mRecentlyExecutedRollbacks.get(i);
+ JSONObject element = new JSONObject();
+ element.put("packageName", rollback.targetPackage.packageName);
+ element.put("higherVersionCode", rollback.targetPackage.higherVersion.versionCode);
+ element.put("lowerVersionCode", rollback.targetPackage.lowerVersion.versionCode);
+ array.put(element);
+ }
+
+ PrintWriter pw = new PrintWriter(mRecentlyExecutedRollbacksFile);
+ pw.println(json.toString());
+ pw.close();
+ } catch (IOException | JSONException e) {
+ // TODO: What to do here?
+ Log.e(TAG, "Failed to save recently executed rollbacks", e);
+ }
+ }
+
+ /**
+ * Records that the given package has been recently rolled back.
+ */
+ private void addRecentlyExecutedRollback(RollbackInfo rollback) {
+ // TODO: if the list of rollbacks gets too big, trim it to only those
+ // that are necessary to keep track of.
+ synchronized (mLock) {
+ ensureRollbackDataLoadedLocked();
+ mRecentlyExecutedRollbacks.add(rollback);
+ saveRecentlyExecutedRollbacksLocked();
+ }
+ }
+
+ /**
+ * Notifies an IntentSender of failure.
+ *
+ * @param statusReceiver where to send the failure
+ * @param message the failure message.
+ */
+ private void sendFailure(IntentSender statusReceiver, String message) {
+ Log.e(TAG, message);
+ try {
+ // TODO: More context on which rollback failed?
+ // TODO: More refined failure code?
+ final Intent fillIn = new Intent();
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, message);
+ statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
+ } catch (IntentSender.SendIntentException e) {
+ // Nowhere to send the result back to, so don't bother.
+ }
+ }
+
+ /**
+ * Notifies an IntentSender of success.
+ */
+ private void sendSuccess(IntentSender statusReceiver) {
+ try {
+ final Intent fillIn = new Intent();
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_SUCCESS);
+ statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
+ } catch (IntentSender.SendIntentException e) {
+ // Nowhere to send the result back to, so don't bother.
+ }
+ }
+
+ // Check to see if anything needs expiration, and if so, expire it.
+ // Schedules future expiration as appropriate.
+ // TODO: Handle cases where the user changes time on the device.
+ private void runExpiration() {
+ Instant now = Instant.now();
+ Instant oldest = null;
+ synchronized (mLock) {
+ ensureRollbackDataLoadedLocked();
+
+ Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
+ while (iter.hasNext()) {
+ PackageRollbackData data = iter.next();
+ if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) {
+ iter.remove();
+ removeFile(data.backupDir);
+ } else if (oldest == null || oldest.isAfter(data.timestamp)) {
+ oldest = data.timestamp;
+ }
+ }
+ }
+
+ if (oldest != null) {
+ scheduleExpiration(now.until(oldest.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS),
+ ChronoUnit.MILLIS));
+ }
+ }
+
+ /**
+ * Schedules an expiration check to be run after the given duration in
+ * milliseconds has gone by.
+ */
+ private void scheduleExpiration(long duration) {
+ getHandler().postDelayed(() -> runExpiration(), duration);
+ }
+
+ private Handler getHandler() {
+ return mHandlerThread.getThreadHandler();
+ }
+
+ /**
+ * Called via broadcast by the package manager when a package is being
+ * staged for install with rollback enabled. Called before the package has
+ * been installed.
+ *
+ * @param id the id of the enable rollback request
+ * @param installFlags information about what is being installed.
+ * @param newPackageCodePath path to the package about to be installed.
+ * @return true if enabling the rollback succeeds, false otherwise.
+ */
+ private boolean enableRollback(int installFlags, File newPackageCodePath) {
+ if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
+ Log.e(TAG, "Rollbacks not supported for instant app install");
+ return false;
+ }
+ if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
+ Log.e(TAG, "Rollbacks not supported for apex install");
+ return false;
+ }
+
+ // Get information about the package to be installed.
+ PackageParser.PackageLite newPackage = null;
+ try {
+ newPackage = PackageParser.parsePackageLite(newPackageCodePath, 0);
+ } catch (PackageParser.PackageParserException e) {
+ Log.e(TAG, "Unable to parse new package", e);
+ return false;
+ }
+
+ String packageName = newPackage.packageName;
+ Log.i(TAG, "Enabling rollback for install of " + packageName);
+
+ PackageRollbackInfo.PackageVersion newVersion =
+ new PackageRollbackInfo.PackageVersion(newPackage.versionCode);
+
+ // Get information about the currently installed package.
+ PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
+ PackageParser.Package installedPackage = pm.getPackage(packageName);
+ if (installedPackage == null) {
+ // TODO: Support rolling back fresh package installs rather than
+ // fail here. Test this case.
+ Log.e(TAG, packageName + " is not installed");
+ return false;
+ }
+ PackageRollbackInfo.PackageVersion installedVersion =
+ new PackageRollbackInfo.PackageVersion(installedPackage.getLongVersionCode());
+
+ File backupDir;
+ try {
+ backupDir = Files.createTempDirectory(
+ mAvailableRollbacksDir.toPath(), packageName + "-").toFile();
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to create rollback for " + packageName, e);
+ return false;
+ }
+
+ // TODO: Should the timestamp be for when we commit the install, not
+ // when we create the pending one?
+ Instant timestamp = Instant.now();
+ try {
+ JSONObject json = new JSONObject();
+ json.put("packageName", packageName);
+ json.put("higherVersionCode", newVersion.versionCode);
+ json.put("lowerVersionCode", installedVersion.versionCode);
+ json.put("timestamp", timestamp.toString());
+
+ File jsonFile = new File(backupDir, "rollback.json");
+ PrintWriter pw = new PrintWriter(jsonFile);
+ pw.println(json.toString());
+ pw.close();
+ } catch (IOException | JSONException e) {
+ Log.e(TAG, "Unable to create rollback for " + packageName, e);
+ removeFile(backupDir);
+ return false;
+ }
+
+ // TODO: Copy by hard link instead to save on cpu and storage space?
+ int status = PackageManagerServiceUtils.copyPackage(installedPackage.codePath, backupDir);
+ if (status != PackageManager.INSTALL_SUCCEEDED) {
+ Log.e(TAG, "Unable to copy package for rollback for " + packageName);
+ removeFile(backupDir);
+ return false;
+ }
+
+ PackageRollbackData data = new PackageRollbackData(
+ new PackageRollbackInfo(packageName, newVersion, installedVersion),
+ backupDir, timestamp);
+
+ synchronized (mLock) {
+ ensureRollbackDataLoadedLocked();
+ mAvailableRollbacks.add(data);
+ }
+
+ return true;
+ }
+
+ // TODO: Don't copy this from PackageManagerShellCommand like this?
+ private static class LocalIntentReceiver {
+ private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
+
+ private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+ @Override
+ public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+ IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+ try {
+ mResult.offer(intent, 5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ public IntentSender getIntentSender() {
+ return new IntentSender((IIntentSender) mLocalSender);
+ }
+
+ public Intent getResult() {
+ try {
+ return mResult.take();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Deletes a file completely.
+ * If the file is a directory, its contents are deleted as well.
+ * Has no effect if the directory does not exist.
+ */
+ private void removeFile(File file) {
+ if (file.isDirectory()) {
+ for (File child : file.listFiles()) {
+ removeFile(child);
+ }
+ }
+ if (file.exists()) {
+ file.delete();
+ }
+ }
+
+ /**
+ * Gets the version of the package currently installed.
+ * Returns null if the package is not currently installed.
+ */
+ private PackageRollbackInfo.PackageVersion getInstalledPackageVersion(String packageName) {
+ PackageManager pm = mContext.getPackageManager();
+ PackageInfo pkgInfo = null;
+ try {
+ pkgInfo = pm.getPackageInfo(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+
+ return new PackageRollbackInfo.PackageVersion(pkgInfo.getLongVersionCode());
+ }
+}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
similarity index 86%
rename from services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
rename to services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
index 4908964..438c303 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
+++ b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
@@ -30,7 +30,7 @@
import java.util.Map;
import java.util.Set;
-class SignedConfigApplicator {
+class GlobalSettingsConfigApplicator {
private static final String TAG = "SignedConfig";
@@ -68,7 +68,7 @@
private final String mSourcePackage;
private final SignatureVerifier mVerifier;
- SignedConfigApplicator(Context context, String sourcePackage) {
+ GlobalSettingsConfigApplicator(Context context, String sourcePackage) {
mContext = context;
mSourcePackage = sourcePackage;
mVerifier = new SignatureVerifier();
@@ -102,7 +102,7 @@
void applyConfig(String configStr, String signature) {
if (!checkSignature(configStr, signature)) {
- Slog.e(TAG, "Signature check on signed configuration in package " + mSourcePackage
+ Slog.e(TAG, "Signature check on global settings in package " + mSourcePackage
+ " failed; ignoring");
return;
}
@@ -110,26 +110,26 @@
try {
config = SignedConfig.parse(configStr, ALLOWED_KEYS, KEY_VALUE_MAPPERS);
} catch (InvalidConfigException e) {
- Slog.e(TAG, "Failed to parse config from package " + mSourcePackage, e);
+ Slog.e(TAG, "Failed to parse global settings from package " + mSourcePackage, e);
return;
}
int currentVersion = getCurrentConfigVersion();
if (currentVersion >= config.version) {
- Slog.i(TAG, "Config from package " + mSourcePackage + " is older than existing: "
- + config.version + "<=" + currentVersion);
+ Slog.i(TAG, "Global settings from package " + mSourcePackage
+ + " is older than existing: " + config.version + "<=" + currentVersion);
return;
}
// We have new config!
- Slog.i(TAG, "Got new signed config from package " + mSourcePackage + ": version "
+ Slog.i(TAG, "Got new global settings from package " + mSourcePackage + ": version "
+ config.version + " replacing existing version " + currentVersion);
SignedConfig.PerSdkConfig matchedConfig =
config.getMatchingConfig(Build.VERSION.SDK_INT);
if (matchedConfig == null) {
- Slog.i(TAG, "Config is not applicable to current SDK version; ignoring");
+ Slog.i(TAG, "Settings is not applicable to current SDK version; ignoring");
return;
}
- Slog.i(TAG, "Updating signed config to version " + config.version);
+ Slog.i(TAG, "Updating global settings to version " + config.version);
updateCurrentConfig(config.version, matchedConfig.values);
}
}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
index 84ce93f..6bcee14 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigService.java
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
@@ -42,8 +42,8 @@
private static final String TAG = "SignedConfig";
// TODO should these be elsewhere? In a public API?
- private static final String KEY_CONFIG = "android.signedconfig";
- private static final String KEY_CONFIG_SIGNATURE = "android.signedconfig.signature";
+ private static final String KEY_GLOBAL_SETTINGS = "android.settings.global";
+ private static final String KEY_GLOBAL_SETTINGS_SIGNATURE = "android.settings.global.signature";
private static class UpdateReceiver extends BroadcastReceiver {
@Override
@@ -80,25 +80,25 @@
if (DBG) Slog.d(TAG, "handlePackageBroadcast: no metadata");
return;
}
- if (metaData.containsKey(KEY_CONFIG)
- && metaData.containsKey(KEY_CONFIG_SIGNATURE)) {
- String config = metaData.getString(KEY_CONFIG);
- String signature = metaData.getString(KEY_CONFIG_SIGNATURE);
+ if (metaData.containsKey(KEY_GLOBAL_SETTINGS)
+ && metaData.containsKey(KEY_GLOBAL_SETTINGS_SIGNATURE)) {
+ String config = metaData.getString(KEY_GLOBAL_SETTINGS);
+ String signature = metaData.getString(KEY_GLOBAL_SETTINGS_SIGNATURE);
try {
// Base64 encoding is standard (not URL safe) encoding: RFC4648
config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8);
} catch (IllegalArgumentException iae) {
- Slog.e(TAG, "Failed to base64 decode config from " + packageName);
+ Slog.e(TAG, "Failed to base64 decode global settings config from " + packageName);
return;
}
if (DBG) {
- Slog.d(TAG, "Got signed config: " + config);
- Slog.d(TAG, "Got config signature: " + signature);
+ Slog.d(TAG, "Got global settings config: " + config);
+ Slog.d(TAG, "Got global settings signature: " + signature);
}
- new SignedConfigApplicator(mContext, packageName).applyConfig(
+ new GlobalSettingsConfigApplicator(mContext, packageName).applyConfig(
config, signature);
} else {
- if (DBG) Slog.d(TAG, "Package has no config/signature.");
+ if (DBG) Slog.d(TAG, "Package has no global settings config/signature.");
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index 973499f..1f638c7 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -37,6 +37,7 @@
import static com.android.server.am.ActivityDisplayProto.FOCUSED_STACK_ID;
import static com.android.server.am.ActivityDisplayProto.ID;
import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY;
+import static com.android.server.am.ActivityDisplayProto.SINGLE_TASK_INSTANCE;
import static com.android.server.am.ActivityDisplayProto.STACKS;
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
@@ -120,6 +121,9 @@
*/
private boolean mRemoved;
+ /** The display can only contain one task. */
+ private boolean mSingleTaskInstance;
+
/**
* A focusable stack that is purposely to be positioned at the top. Although the stack may not
* have the topmost index, it is used as a preferred candidate to prevent being unable to resume
@@ -244,6 +248,10 @@
final ActivityStack prevFocusedStack = updateLastFocusedStackReason != null
? getFocusedStack() : null;
final boolean wasContained = mStacks.remove(stack);
+ if (mSingleTaskInstance && getChildCount() > 0) {
+ throw new IllegalStateException(
+ "positionChildAt: Can only have one child on display=" + this);
+ }
final int insertPosition = getTopInsertPosition(stack, position);
mStacks.add(insertPosition, stack);
@@ -403,6 +411,14 @@
*/
<T extends ActivityStack> T createStack(int windowingMode, int activityType, boolean onTop) {
+ if (mSingleTaskInstance && getChildCount() > 0) {
+ // Create stack on default display instead since this display can only contain 1 stack.
+ // TODO: Kinda a hack, but better that having the decision at each call point. Hoping
+ // this goes away once ActivityView is no longer using virtual displays.
+ return mRootActivityContainer.getDefaultDisplay().createStack(
+ windowingMode, activityType, onTop);
+ }
+
if (activityType == ACTIVITY_TYPE_UNDEFINED) {
// Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
// anything else should be passing it in anyways...
@@ -1337,8 +1353,31 @@
}
}
+ void setDisplayToSingleTaskInstance() {
+ final int childCount = getChildCount();
+ if (childCount > 1) {
+ throw new IllegalArgumentException("Display already has multiple stacks. display="
+ + this);
+ }
+ if (childCount > 0) {
+ final ActivityStack stack = getChildAt(0);
+ if (stack.getChildCount() > 1) {
+ throw new IllegalArgumentException("Display stack already has multiple tasks."
+ + " display=" + this + " stack=" + stack);
+ }
+ }
+
+ mSingleTaskInstance = true;
+ }
+
+ /** Returns true if the display can only contain one task */
+ boolean isSingleTaskInstance() {
+ return mSingleTaskInstance;
+ }
+
public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + mStacks.size());
+ pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + mStacks.size()
+ + (mSingleTaskInstance ? " mSingleTaskInstance" : ""));
final String myPrefix = prefix + " ";
if (mHomeStack != null) {
pw.println(myPrefix + "mHomeStack=" + mHomeStack);
@@ -1373,6 +1412,7 @@
final long token = proto.start(fieldId);
super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
proto.write(ID, mDisplayId);
+ proto.write(SINGLE_TASK_INSTANCE, mSingleTaskInstance);
final ActivityStack focusedStack = getFocusedStack();
if (focusedStack != null) {
proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2cd0168..8884615 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -271,7 +271,10 @@
boolean fullscreen; // The activity is opaque and fills the entire space of this task.
// TODO: See if it possible to combine this with the fullscreen field.
final boolean hasWallpaper; // Has a wallpaper window as a background.
- final boolean noDisplay; // activity is not displayed?
+ @VisibleForTesting
+ boolean noDisplay; // activity is not displayed?
+ @VisibleForTesting
+ int mHandoverLaunchDisplayId = INVALID_DISPLAY; // Handover launch display id to next activity.
private final boolean componentSpecified; // did caller specify an explicit component?
final boolean rootVoiceInteraction; // was this the root activity of a voice interaction?
@@ -1019,6 +1022,8 @@
if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
}
+ // Gets launch display id from options. It returns INVALID_DISPLAY if not set.
+ mHandoverLaunchDisplayId = options.getLaunchDisplayId();
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 49cf8c7..4581a0f 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1144,6 +1144,12 @@
}
}
+ /** @return true if the stack can only contain one task */
+ boolean isSingleTaskInstance() {
+ final ActivityDisplay display = getDisplay();
+ return display != null && display.isSingleTaskInstance();
+ }
+
final void removeActivitiesFromLRUListLocked(TaskRecord task) {
for (ActivityRecord r : task.mActivities) {
mLRUActivities.remove(r);
@@ -5369,6 +5375,10 @@
String reason) {
// TODO: Is this remove really needed? Need to look into the call path for the other addTask
mTaskHistory.remove(task);
+ if (isSingleTaskInstance() && !mTaskHistory.isEmpty()) {
+ throw new IllegalStateException("Can only have one child on stack=" + this);
+ }
+
position = getAdjustedPositionForTask(task, position, null /* starting */);
final boolean toTop = position >= mTaskHistory.size();
final ActivityStack prevStack = preAddTask(task, reason, toTop);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index c4be1ba537..86c5d4d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4569,6 +4569,26 @@
}
}
+ /**
+ * Makes the display with the given id a single task instance display. I.e the display can only
+ * contain one task.
+ */
+ @Override
+ public void setDisplayToSingleTaskInstance(int displayId) {
+ mAmInternal.enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "setDisplayToSingleTaskInstance");
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ final ActivityDisplay display =
+ mRootActivityContainer.getActivityDisplayOrCreate(displayId);
+ if (display != null) {
+ display.setDisplayToSingleTaskInstance();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
void dumpLastANRLocked(PrintWriter pw) {
pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
if (mLastANRState == null) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index d8b2b52..a5341ca 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1949,6 +1949,11 @@
} else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
} else if (taskSwitch && allowTaskSnapshot) {
+ if (mWmService.mLowRamTaskSnapshots) {
+ // For low RAM devices, we use the splash screen starting window instead of the
+ // task snapshot starting window.
+ return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ }
return snapshot == null ? STARTING_WINDOW_TYPE_NONE
: snapshotOrientationSameAsTask(snapshot) || fromRecents
? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index 5b20af3..9bc8462 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -62,7 +62,8 @@
private StatusBarManagerInternal mStatusBarInternal;
protected WindowState mWin;
- private int mState = StatusBarManager.WINDOW_STATE_SHOWING;
+ private @StatusBarManager.WindowVisibleState int mState =
+ StatusBarManager.WINDOW_STATE_SHOWING;
private int mTransientBarState;
private boolean mPendingShow;
private long mLastTranslucent;
@@ -199,7 +200,8 @@
return !mWin.isDrawnLw();
}
- private int computeStateLw(boolean wasVis, boolean wasAnim, WindowState win, boolean change) {
+ private @StatusBarManager.WindowVisibleState int computeStateLw(
+ boolean wasVis, boolean wasAnim, WindowState win, boolean change) {
if (win.isDrawnLw()) {
final boolean vis = win.isVisibleLw();
final boolean anim = win.isAnimatingLw();
@@ -218,7 +220,7 @@
return mState;
}
- private boolean updateStateLw(final int state) {
+ private boolean updateStateLw(@StatusBarManager.WindowVisibleState final int state) {
if (mWin != null && state != mState) {
mState = state;
if (DEBUG) Slog.d(mTag, "mState: " + StatusBarManager.windowStateToString(state));
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a5ceee2..fecc8da 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2506,6 +2506,7 @@
mWmService.mAnimator.removeDisplayLocked(mDisplayId);
mWindowingLayer.release();
mOverlayLayer.release();
+ mInputMonitor.onDisplayRemoved();
} finally {
mDisplayReady = false;
mRemovingDisplay = false;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index fc1c65c..632db38 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -32,6 +32,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
@@ -45,7 +46,9 @@
import android.view.InputEventReceiver;
import android.view.InputWindowHandle;
import android.view.SurfaceControl;
+import android.view.animation.Animation;
+import com.android.server.AnimationThread;
import com.android.server.policy.WindowManagerPolicy;
import java.io.PrintWriter;
@@ -60,6 +63,7 @@
// When true, need to call updateInputWindowsLw().
private boolean mUpdateInputWindowsNeeded = true;
+ private boolean mUpdateInputWindowsPending;
// Currently focused input window handle.
private InputWindowHandle mFocusedInputWindowHandle;
@@ -70,8 +74,11 @@
new UpdateInputForAllWindowsConsumer();
private final int mDisplayId;
+ private final DisplayContent mDisplayContent;
+ private boolean mDisplayRemoved;
private final SurfaceControl.Transaction mInputTransaction;
+ private final Handler mHandler;
/**
* The set of input consumer added to the window manager by name, which consumes input events
@@ -105,10 +112,64 @@
}
}
+ private final Runnable mUpdateInputWindows = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mService.mGlobalLock) {
+ mUpdateInputWindowsPending = false;
+ mUpdateInputWindowsNeeded = false;
+
+ if (mDisplayRemoved) {
+ return;
+ }
+
+ // Populate the input window list with information about all of the windows that
+ // could potentially receive input.
+ // As an optimization, we could try to prune the list of windows but this turns
+ // out to be difficult because only the native code knows for sure which window
+ // currently has touch focus.
+
+ // If there's a drag in flight, provide a pseudo-window to catch drag input
+ final boolean inDrag = mService.mDragDropController.dragDropActiveLocked();
+ if (inDrag) {
+ if (DEBUG_DRAG) {
+ Log.d(TAG_WM, "Inserting drag window");
+ }
+ mService.mDragDropController.showInputSurface(mInputTransaction, mDisplayId);
+ } else {
+ mService.mDragDropController.hideInputSurface(mInputTransaction, mDisplayId);
+ }
+
+ final boolean inPositioning =
+ mService.mTaskPositioningController.isPositioningLocked();
+ if (inPositioning) {
+ if (DEBUG_TASK_POSITIONING) {
+ Log.d(TAG_WM, "Inserting window handle for repositioning");
+ }
+ mService.mTaskPositioningController.showInputSurface(mInputTransaction,
+ mDisplayId);
+ } else {
+ mService.mTaskPositioningController.hideInputSurface(mInputTransaction,
+ mDisplayId);
+ }
+
+ // Add all windows on the default display.
+ mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);
+ }
+ }
+ };
+
public InputMonitor(WindowManagerService service, int displayId) {
mService = service;
+ mDisplayContent = mService.mRoot.getDisplayContent(displayId);
mDisplayId = displayId;
- mInputTransaction = mService.mRoot.getDisplayContent(mDisplayId).getPendingTransaction();
+ mInputTransaction = mDisplayContent.getPendingTransaction();
+ mHandler = AnimationThread.getHandler();
+ }
+
+ void onDisplayRemoved() {
+ mHandler.removeCallbacks(mUpdateInputWindows);
+ mDisplayRemoved = true;
}
private void addInputConsumer(String name, InputConsumerImpl consumer) {
@@ -248,41 +309,18 @@
if (!force && !mUpdateInputWindowsNeeded) {
return;
}
- mUpdateInputWindowsNeeded = false;
+ scheduleUpdateInputWindows();
+ }
- if (false) Slog.d(TAG_WM, ">>>>>> ENTERED updateInputWindowsLw");
-
- // Populate the input window list with information about all of the windows that
- // could potentially receive input.
- // As an optimization, we could try to prune the list of windows but this turns
- // out to be difficult because only the native code knows for sure which window
- // currently has touch focus.
-
- // If there's a drag in flight, provide a pseudo-window to catch drag input
- final boolean inDrag = mService.mDragDropController.dragDropActiveLocked();
- if (inDrag) {
- if (DEBUG_DRAG) {
- Log.d(TAG_WM, "Inserting drag window");
- }
- mService.mDragDropController.showInputSurface(mInputTransaction, mDisplayId);
- } else {
- mService.mDragDropController.hideInputSurface(mInputTransaction, mDisplayId);
+ private void scheduleUpdateInputWindows() {
+ if (mDisplayRemoved) {
+ return;
}
- final boolean inPositioning = mService.mTaskPositioningController.isPositioningLocked();
- if (inPositioning) {
- if (DEBUG_TASK_POSITIONING) {
- Log.d(TAG_WM, "Inserting window handle for repositioning");
- }
- mService.mTaskPositioningController.showInputSurface(mInputTransaction, mDisplayId);
- } else {
- mService.mTaskPositioningController.hideInputSurface(mInputTransaction, mDisplayId);
+ if (!mUpdateInputWindowsPending) {
+ mUpdateInputWindowsPending = true;
+ mHandler.post(mUpdateInputWindows);
}
-
- // Add all windows on the default display.
- mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);
-
- if (false) Slog.d(TAG_WM, "<<<<<<< EXITED updateInputWindowsLw");
}
/* Called when the current input focus changes.
@@ -385,19 +423,18 @@
mTmpRect.setEmpty();
mDisableWallpaperTouchEvents = false;
this.inDrag = inDrag;
- final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
- wallpaperController = dc.mWallpaperController;
+ wallpaperController = mDisplayContent.mWallpaperController;
resetInputConsumers(mInputTransaction);
- dc.forAllWindows(this,
+ mDisplayContent.forAllWindows(this,
true /* traverseTopToBottom */);
if (mAddWallpaperInputConsumerHandle) {
wallpaperInputConsumer.show(mInputTransaction, 0);
}
- dc.scheduleAnimation();
+ mDisplayContent.scheduleAnimation();
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index c63ee3e..f55c7c9 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -913,6 +913,13 @@
+ " to its current displayId=" + displayId);
}
+ if (activityDisplay.isSingleTaskInstance() && activityDisplay.getChildCount() > 0) {
+ // We don't allow moving stacks to single instance display that already has a child.
+ Slog.e(TAG, "Can not move stack=" + stack
+ + " to single task instance display=" + activityDisplay);
+ return;
+ }
+
stack.reparent(activityDisplay, onTop, false /* displayRemoved */);
// TODO(multi-display): resize stacks properly if moved from split-screen.
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 5107b52..da63dc9 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -305,6 +305,13 @@
displayId = optionLaunchId;
}
+ // If the source activity is a no-display activity, pass on the launch display id from
+ // source activity as currently preferred.
+ if (displayId == INVALID_DISPLAY && source != null && source.noDisplay) {
+ displayId = source.mHandoverLaunchDisplayId;
+ if (DEBUG) appendLog("display-from-no-display-source=" + displayId);
+ }
+
ActivityStack stack =
(displayId == INVALID_DISPLAY && task != null) ? task.getStack() : null;
if (stack != null) {
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index f1b0c0696..e343322 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -911,7 +911,6 @@
info.packageName, info.targetActivity);
if (_intent != null) {
Intent targetIntent = new Intent(_intent);
- targetIntent.setComponent(targetComponent);
targetIntent.setSelector(null);
targetIntent.setSourceBounds(null);
if (DEBUG_TASKS) Slog.v(TAG_TASKS,
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b6a4a51..f5f55e2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -433,6 +433,12 @@
final long mDrawLockTimeoutMillis;
final boolean mAllowAnimationsInLowPowerMode;
+ /**
+ * Use very low resolution task snapshots. Replaces task snapshot starting windows with
+ * splashscreen starting windows. Used on low RAM devices to save memory.
+ */
+ final boolean mLowRamTaskSnapshots;
+
final boolean mAllowBootMessages;
final boolean mLimitedAlphaCompositing;
@@ -949,6 +955,8 @@
com.android.internal.R.bool.config_disableTransitionAnimation);
mPerDisplayFocusEnabled = context.getResources().getBoolean(
com.android.internal.R.bool.config_perDisplayFocusEnabled);
+ mLowRamTaskSnapshots = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_lowRamTaskSnapshotsAndRecents);
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mDisplayWindowSettings = new DisplayWindowSettings(this);
diff --git a/services/core/jni/BroadcastRadio/NativeCallbackThread.h b/services/core/jni/BroadcastRadio/NativeCallbackThread.h
index 53990be..0f62de9 100644
--- a/services/core/jni/BroadcastRadio/NativeCallbackThread.h
+++ b/services/core/jni/BroadcastRadio/NativeCallbackThread.h
@@ -41,7 +41,7 @@
DISALLOW_COPY_AND_ASSIGN(NativeCallbackThread);
public:
- NativeCallbackThread(JavaVM *vm);
+ explicit NativeCallbackThread(JavaVM *vm);
virtual ~NativeCallbackThread();
void enqueue(const Task &task);
diff --git a/services/core/jni/BroadcastRadio/Tuner.cpp b/services/core/jni/BroadcastRadio/Tuner.cpp
index 9c2e1e5..a2a7f7d 100644
--- a/services/core/jni/BroadcastRadio/Tuner.cpp
+++ b/services/core/jni/BroadcastRadio/Tuner.cpp
@@ -73,7 +73,8 @@
wp<V1_1::ITunerCallback> mTunerCallback;
public:
- HalDeathRecipient(wp<V1_1::ITunerCallback> tunerCallback):mTunerCallback(tunerCallback) {}
+ explicit HalDeathRecipient(wp<V1_1::ITunerCallback> tunerCallback)
+ : mTunerCallback(tunerCallback) {}
virtual void serviceDied(uint64_t cookie, const wp<hidl::base::V1_0::IBase>& who);
};
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
index c22109c..b08d13f 100644
--- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
@@ -89,7 +89,7 @@
private:
class HdmiCecCallback : public IHdmiCecCallback {
public:
- HdmiCecCallback(HdmiCecController* controller) : mController(controller) {};
+ explicit HdmiCecCallback(HdmiCecController* controller) : mController(controller) {};
Return<void> onCecMessage(const CecMessage& event) override;
Return<void> onHotplugEvent(const HotplugEvent& event) override;
private:
diff --git a/services/core/jni/com_android_server_storage_AppFuseBridge.cpp b/services/core/jni/com_android_server_storage_AppFuseBridge.cpp
index c8f842d..e519633 100644
--- a/services/core/jni/com_android_server_storage_AppFuseBridge.cpp
+++ b/services/core/jni/com_android_server_storage_AppFuseBridge.cpp
@@ -74,7 +74,7 @@
}
}
- operator bool() {
+ explicit operator bool() {
return mLocked;
}
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 6c2a894..098b2ef 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -292,7 +292,7 @@
class TvInputCallback : public ITvInputCallback {
public:
- TvInputCallback(JTvInputHal* hal);
+ explicit TvInputCallback(JTvInputHal* hal);
Return<void> notify(const TvInputEvent& event) override;
private:
JTvInputHal* mHal;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 855fb50..be09aea 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -783,7 +783,7 @@
// Manages apk rollbacks.
traceBeginAndSlog("StartRollbackManagerService");
- mSystemServiceManager.startService(RollbackManagerService.Lifecycle.class);
+ mSystemServiceManager.startService(RollbackManagerService.class);
traceEnd();
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index afbe6bc..9d84751 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -1015,6 +1015,22 @@
}
@Test
+ public void testCanceledNoisyNeverVibrate() throws Exception {
+ NotificationRecord r = getBuzzyBeepyNotification();
+
+ final int waitMs = mAudioManager.getFocusRampTimeMs(
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
+ r.getAudioAttributes());
+
+ mService.buzzBeepBlinkLocked(r);
+ mService.clearNotifications();
+
+ verifyNeverVibrate();
+ Thread.sleep(waitMs);
+ verifyNeverVibrate();
+ }
+
+ @Test
public void testEmptyUriSoundTreatedAsNoSound() throws Exception {
NotificationChannel channel = new NotificationChannel("test", "test", IMPORTANCE_HIGH);
channel.setSound(Uri.EMPTY, null);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 659c6e7..8b65e76 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -859,6 +859,19 @@
assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("c/c")));
}
+ @Test
+ public void testOnPackagesChanged_nullValuesPassed_noNullPointers() {
+ for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+ mIpm, approvalLevel);
+ // null uid list
+ service.onPackagesChanged(true, new String[]{"this.is.a.package.name"}, null);
+
+ // null package list
+ service.onPackagesChanged(true, null, new int[]{103});
+ }
+ }
+
private void loadXml(ManagedServices service) throws Exception {
final StringBuffer xml = new StringBuffer();
xml.append("<" + service.getConfig().xmlTag + ">\n");
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 7186e22..41cdcc0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -199,6 +199,25 @@
assertEquals(freeformDisplay.mDisplayId, mResult.mPreferredDisplayId);
}
+ @Test
+ public void testUsesNoDisplaySourceHandoverDisplayIdIfSet() {
+ final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+ WINDOWING_MODE_FREEFORM);
+ final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay(
+ WINDOWING_MODE_FULLSCREEN);
+
+ mCurrent.mPreferredDisplayId = fullscreenDisplay.mDisplayId;
+ ActivityRecord reusableActivity = createSourceActivity(fullscreenDisplay);
+ ActivityRecord source = createSourceActivity(freeformDisplay);
+ source.mHandoverLaunchDisplayId = freeformDisplay.mDisplayId;
+ source.noDisplay = true;
+
+ assertEquals(RESULT_CONTINUE, mTarget.onCalculate(reusableActivity.getTaskRecord(),
+ null /* layout */, mActivity, source, null /* options */, mCurrent, mResult));
+
+ assertEquals(freeformDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ }
+
// =====================================
// Launch Windowing Mode Related Tests
// =====================================
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 6b6b33c..7da85af 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -34,6 +34,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -180,6 +181,24 @@
mParentBounds);
}
+ /** Ensures that the alias intent won't have target component resolved. */
+ @Test
+ public void testTaskIntentActivityAlias() {
+ final String aliasActivity = DEFAULT_COMPONENT_PACKAGE_NAME + ".aliasActivity";
+ final String targetActivity = DEFAULT_COMPONENT_PACKAGE_NAME + ".targetActivity";
+ final Intent intent = new Intent();
+ intent.setComponent(new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasActivity));
+ final ActivityInfo info = new ActivityInfo();
+ info.applicationInfo = new ApplicationInfo();
+ info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME;
+ info.targetActivity = targetActivity;
+
+ final TaskRecord task = TaskRecord.create(mService, 1 /* taskId */, info, intent,
+ null /* taskDescription */);
+ assertEquals("The alias activity component should be saved in task intent.", aliasActivity,
+ task.intent.getComponent().getClassName());
+ }
+
private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
Rect expectedConfigBounds) {
diff --git a/media/java/android/media/session/SessionCallbackLink.aidl b/telephony/java/android/telephony/ims/Rcs1To1Thread.aidl
similarity index 75%
copy from media/java/android/media/session/SessionCallbackLink.aidl
copy to telephony/java/android/telephony/ims/Rcs1To1Thread.aidl
index 9c0ae05..9fdc41d 100644
--- a/media/java/android/media/session/SessionCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/Rcs1To1Thread.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable SessionCallbackLink;
+package android.telephony.ims;
+
+parcelable Rcs1To1Thread;
diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.java b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
new file mode 100644
index 0000000..5122c7d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
@@ -0,0 +1,49 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * Rcs1To1Thread represents a single RCS conversation thread with a total of two
+ * {@link RcsParticipant}s.
+ * @hide - TODO(sahinc) make this public
+ */
+public class Rcs1To1Thread extends RcsThread {
+ public static final Creator<Rcs1To1Thread> CREATOR = new Creator<Rcs1To1Thread>() {
+ @Override
+ public Rcs1To1Thread createFromParcel(Parcel in) {
+ return new Rcs1To1Thread(in);
+ }
+
+ @Override
+ public Rcs1To1Thread[] newArray(int size) {
+ return new Rcs1To1Thread[size];
+ }
+ };
+
+ protected Rcs1To1Thread(Parcel in) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+}
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsFileTransferPart.aidl
similarity index 74%
copy from media/java/android/media/session/ControllerCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsFileTransferPart.aidl
index 788f5d3..eaf3128 100644
--- a/media/java/android/media/session/ControllerCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsFileTransferPart.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable ControllerCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsFileTransferPart;
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.java b/telephony/java/android/telephony/ims/RcsFileTransferPart.java
new file mode 100644
index 0000000..39c58dd
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsFileTransferPart.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * A part of a composite {@link RcsMessage} that holds a file transfer.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsFileTransferPart extends RcsPart {
+ public static final Creator<RcsFileTransferPart> CREATOR = new Creator<RcsFileTransferPart>() {
+ @Override
+ public RcsFileTransferPart createFromParcel(Parcel in) {
+ return new RcsFileTransferPart(in);
+ }
+
+ @Override
+ public RcsFileTransferPart[] newArray(int size) {
+ return new RcsFileTransferPart[size];
+ }
+ };
+
+ protected RcsFileTransferPart(Parcel in) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+}
diff --git a/media/java/android/media/session/SessionCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsGroupThread.aidl
similarity index 75%
copy from media/java/android/media/session/SessionCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsGroupThread.aidl
index 9c0ae05..c4ce529 100644
--- a/media/java/android/media/session/SessionCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsGroupThread.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable SessionCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsGroupThread;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.java b/telephony/java/android/telephony/ims/RcsGroupThread.java
new file mode 100644
index 0000000..150c4d4
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThread.java
@@ -0,0 +1,49 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * RcsGroupThread represents a single RCS conversation thread where {@link RcsParticipant}s can join
+ * or leave.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsGroupThread extends RcsThread {
+ public static final Creator<RcsGroupThread> CREATOR = new Creator<RcsGroupThread>() {
+ @Override
+ public RcsGroupThread createFromParcel(Parcel in) {
+ return new RcsGroupThread(in);
+ }
+
+ @Override
+ public RcsGroupThread[] newArray(int size) {
+ return new RcsGroupThread[size];
+ }
+ };
+
+ protected RcsGroupThread(Parcel in) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+}
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsIncomingMessage.aidl
similarity index 74%
copy from media/java/android/media/session/ControllerCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsIncomingMessage.aidl
index 788f5d3..6552a82 100644
--- a/media/java/android/media/session/ControllerCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessage.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable ControllerCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsIncomingMessage;
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.java b/telephony/java/android/telephony/ims/RcsIncomingMessage.java
new file mode 100644
index 0000000..f39e06d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessage.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * This is a single instance of a message received over RCS.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsIncomingMessage extends RcsMessage {
+ public static final Creator<RcsIncomingMessage> CREATOR = new Creator<RcsIncomingMessage>() {
+ @Override
+ public RcsIncomingMessage createFromParcel(Parcel in) {
+ return new RcsIncomingMessage(in);
+ }
+
+ @Override
+ public RcsIncomingMessage[] newArray(int size) {
+ return new RcsIncomingMessage[size];
+ }
+ };
+
+ protected RcsIncomingMessage(Parcel in) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+}
diff --git a/media/java/android/media/session/SessionCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsLocationPart.aidl
similarity index 75%
copy from media/java/android/media/session/SessionCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsLocationPart.aidl
index 9c0ae05..4fe5ca9 100644
--- a/media/java/android/media/session/SessionCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsLocationPart.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable SessionCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsLocationPart;
diff --git a/telephony/java/android/telephony/ims/RcsLocationPart.java b/telephony/java/android/telephony/ims/RcsLocationPart.java
new file mode 100644
index 0000000..19be4ce
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsLocationPart.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * A part of a composite {@link RcsMessage} that holds a location
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsLocationPart extends RcsPart {
+ public static final Creator<RcsLocationPart> CREATOR = new Creator<RcsLocationPart>() {
+ @Override
+ public RcsLocationPart createFromParcel(Parcel in) {
+ return new RcsLocationPart(in);
+ }
+
+ @Override
+ public RcsLocationPart[] newArray(int size) {
+ return new RcsLocationPart[size];
+ }
+ };
+
+ protected RcsLocationPart(Parcel in) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+}
diff --git a/media/java/android/media/session/SessionCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsManager.aidl
similarity index 75%
copy from media/java/android/media/session/SessionCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsManager.aidl
index 9c0ae05..63bc71c 100644
--- a/media/java/android/media/session/SessionCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsManager.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable SessionCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsManager;
diff --git a/telephony/java/android/telephony/ims/RcsManager.java b/telephony/java/android/telephony/ims/RcsManager.java
index d50b516..df108c8 100644
--- a/telephony/java/android/telephony/ims/RcsManager.java
+++ b/telephony/java/android/telephony/ims/RcsManager.java
@@ -1,5 +1,5 @@
/*
- * 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.
diff --git a/media/java/android/media/session/SessionCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsMessage.aidl
similarity index 75%
rename from media/java/android/media/session/SessionCallbackLink.aidl
rename to telephony/java/android/telephony/ims/RcsMessage.aidl
index 9c0ae05..b32cd12 100644
--- a/media/java/android/media/session/SessionCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsMessage.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * Copyright 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable SessionCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsMessage;
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsMessage.java
similarity index 65%
copy from media/java/android/media/session/ControllerCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsMessage.java
index 788f5d3..d46685c 100644
--- a/media/java/android/media/session/ControllerCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsMessage.java
@@ -1,5 +1,5 @@
/*
- * Copyright 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.
@@ -13,6 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
+package android.telephony.ims;
-parcelable ControllerCallbackLink;
+import android.os.Parcelable;
+
+/**
+ * This is a single instance of a message sent or received over RCS.
+ * @hide - TODO(sahinc) make this public
+ */
+public abstract class RcsMessage implements Parcelable {
+}
diff --git a/telephony/java/android/telephony/ims/RcsMessageStore.java b/telephony/java/android/telephony/ims/RcsMessageStore.java
index c89c0be..4198c78 100644
--- a/telephony/java/android/telephony/ims/RcsMessageStore.java
+++ b/telephony/java/android/telephony/ims/RcsMessageStore.java
@@ -1,5 +1,5 @@
/*
- * 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.
diff --git a/telephony/java/android/telephony/ims/RcsMultiMediaPart.java b/telephony/java/android/telephony/ims/RcsMultiMediaPart.java
new file mode 100644
index 0000000..d295fba
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMultiMediaPart.java
@@ -0,0 +1,50 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * A part of a composite {@link RcsMessage} that holds a media that is rendered on the screen
+ * (i.e. image, video etc)
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsMultiMediaPart extends RcsFileTransferPart {
+ public static final Creator<RcsMultiMediaPart> CREATOR = new Creator<RcsMultiMediaPart>() {
+ @Override
+ public RcsMultiMediaPart createFromParcel(Parcel in) {
+ return new RcsMultiMediaPart(in);
+ }
+
+ @Override
+ public RcsMultiMediaPart[] newArray(int size) {
+ return new RcsMultiMediaPart[size];
+ }
+ };
+
+ protected RcsMultiMediaPart(Parcel in) {
+ super(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+}
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl
similarity index 74%
copy from media/java/android/media/session/ControllerCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsMultimediaPart.aidl
index 788f5d3..5992d95 100644
--- a/media/java/android/media/session/ControllerCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable ControllerCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsMultimediaPart;
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl
similarity index 74%
copy from media/java/android/media/session/ControllerCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl
index 788f5d3..6e0c80f 100644
--- a/media/java/android/media/session/ControllerCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable ControllerCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsOutgoingMessage;
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
new file mode 100644
index 0000000..bfb1611
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * This is a single instance of a message sent over RCS.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsOutgoingMessage extends RcsMessage {
+ public static final Creator<RcsOutgoingMessage> CREATOR = new Creator<RcsOutgoingMessage>() {
+ @Override
+ public RcsOutgoingMessage createFromParcel(Parcel in) {
+ return new RcsOutgoingMessage(in);
+ }
+
+ @Override
+ public RcsOutgoingMessage[] newArray(int size) {
+ return new RcsOutgoingMessage[size];
+ }
+ };
+
+ protected RcsOutgoingMessage(Parcel in) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+}
diff --git a/media/java/android/media/session/SessionCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsPart.aidl
similarity index 75%
copy from media/java/android/media/session/SessionCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsPart.aidl
index 9c0ae05..8b8077d 100644
--- a/media/java/android/media/session/SessionCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsPart.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable SessionCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsPart;
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsPart.java
similarity index 67%
copy from media/java/android/media/session/ControllerCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsPart.java
index 788f5d3..da50173 100644
--- a/media/java/android/media/session/ControllerCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsPart.java
@@ -1,5 +1,5 @@
/*
- * Copyright 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.
@@ -13,6 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
+package android.telephony.ims;
-parcelable ControllerCallbackLink;
+import android.os.Parcelable;
+
+/**
+ * A part of a composite {@link RcsMessage}.
+ * @hide - TODO(sahinc) make this public
+ */
+public abstract class RcsPart implements Parcelable {
+}
diff --git a/media/java/android/media/session/SessionCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsParticipant.aidl
similarity index 75%
copy from media/java/android/media/session/SessionCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsParticipant.aidl
index 9c0ae05..1c44363 100644
--- a/media/java/android/media/session/SessionCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsParticipant.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable SessionCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsParticipant;
diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java
new file mode 100644
index 0000000..318dba3
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsParticipant.java
@@ -0,0 +1,50 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * RcsParticipant is an RCS capable contact that can participate in {@link RcsThread}s.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsParticipant implements Parcelable {
+ public static final Creator<RcsParticipant> CREATOR = new Creator<RcsParticipant>() {
+ @Override
+ public RcsParticipant createFromParcel(Parcel in) {
+ return new RcsParticipant(in);
+ }
+
+ @Override
+ public RcsParticipant[] newArray(int size) {
+ return new RcsParticipant[size];
+ }
+ };
+
+ protected RcsParticipant(Parcel in) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+
+ }
+}
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.aidl
similarity index 73%
copy from media/java/android/media/session/ControllerCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.aidl
index 788f5d3..b9d8190 100644
--- a/media/java/android/media/session/ControllerCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable ControllerCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsParticipantAliasChangedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
new file mode 100644
index 0000000..b9ca5a8
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
@@ -0,0 +1,49 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * An event that indicates an {@link RcsParticipant}'s alias was changed.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsParticipantAliasChangedEvent extends RcsParticipantEvent {
+ public static final Creator<RcsParticipantAliasChangedEvent> CREATOR =
+ new Creator<RcsParticipantAliasChangedEvent>() {
+ @Override
+ public RcsParticipantAliasChangedEvent createFromParcel(Parcel in) {
+ return new RcsParticipantAliasChangedEvent(in);
+ }
+
+ @Override
+ public RcsParticipantAliasChangedEvent[] newArray(int size) {
+ return new RcsParticipantAliasChangedEvent[size];
+ }
+ };
+
+ protected RcsParticipantAliasChangedEvent(Parcel in) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+}
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl
similarity index 74%
copy from media/java/android/media/session/ControllerCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsParticipantEvent.aidl
index 788f5d3..c0a7789 100644
--- a/media/java/android/media/session/ControllerCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable ControllerCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsParticipantEvent;
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsParticipantEvent.java
similarity index 65%
copy from media/java/android/media/session/ControllerCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsParticipantEvent.java
index 788f5d3..371b8b7 100644
--- a/media/java/android/media/session/ControllerCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsParticipantEvent.java
@@ -1,5 +1,5 @@
/*
- * Copyright 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.
@@ -13,6 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
+package android.telephony.ims;
-parcelable ControllerCallbackLink;
+import android.os.Parcelable;
+
+/**
+ * An event that is associated with an {@link RcsParticipant}
+ * @hide - TODO(sahinc) make this public
+ */
+public abstract class RcsParticipantEvent implements Parcelable {
+}
diff --git a/media/java/android/media/session/SessionCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsTextPart.aidl
similarity index 75%
copy from media/java/android/media/session/SessionCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsTextPart.aidl
index 9c0ae05..4f9fe1f 100644
--- a/media/java/android/media/session/SessionCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsTextPart.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable SessionCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsTextPart;
diff --git a/telephony/java/android/telephony/ims/RcsTextPart.java b/telephony/java/android/telephony/ims/RcsTextPart.java
new file mode 100644
index 0000000..2a72df1
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsTextPart.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * A part of a composite {@link RcsMessage} that holds a string
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsTextPart extends RcsPart {
+ public static final Creator<RcsTextPart> CREATOR = new Creator<RcsTextPart>() {
+ @Override
+ public RcsTextPart createFromParcel(Parcel in) {
+ return new RcsTextPart(in);
+ }
+
+ @Override
+ public RcsTextPart[] newArray(int size) {
+ return new RcsTextPart[size];
+ }
+ };
+
+ protected RcsTextPart(Parcel in) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThread.aidl b/telephony/java/android/telephony/ims/RcsThread.aidl
index 79d47326..d9cf6db 100644
--- a/telephony/java/android/telephony/ims/RcsThread.aidl
+++ b/telephony/java/android/telephony/ims/RcsThread.aidl
@@ -1,6 +1,6 @@
/*
*
- * Copyright 2018, The Android Open Source Project
+ * 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.
diff --git a/telephony/java/android/telephony/ims/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java
index b7f440d..2969ffd 100644
--- a/telephony/java/android/telephony/ims/RcsThread.java
+++ b/telephony/java/android/telephony/ims/RcsThread.java
@@ -1,5 +1,5 @@
/*
- * 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.
@@ -16,59 +16,13 @@
package android.telephony.ims;
-import android.os.Parcel;
import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.telephony.ims.aidl.IRcs;
/**
* RcsThread represents a single RCS conversation thread. It holds messages that were sent and
- * received and events that occured on that thread.
+ * received and events that occurred on that thread.
* @hide - TODO(sahinc) make this public
*/
-public class RcsThread implements Parcelable {
- public static final Creator<RcsThread> CREATOR = new Creator<RcsThread>() {
- @Override
- public RcsThread createFromParcel(Parcel in) {
- return new RcsThread(in);
- }
+public abstract class RcsThread implements Parcelable {
- @Override
- public RcsThread[] newArray(int size) {
- return new RcsThread[size];
- }
- };
-
- protected RcsThread(Parcel in) {
- }
-
- /**
- * Returns the number of messages in this RCS thread.
- *
- * @hide
- */
- public int getMessageCount() {
- try {
- IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
- if (iRcs != null) {
- // TODO(sahinc): substitute to the regular thread id once we have database
- // TODO(sahinc): connection in place
- return iRcs.getMessageCount(/* rcsThreadId= */ 123);
- }
- } catch (RemoteException re) {
- // TODO(sahinc): Log something meaningful
- }
- return 0;
- }
-
- /** Implement the Parcelable interface */
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
}
diff --git a/media/java/android/media/session/SessionCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsThreadEvent.aidl
similarity index 75%
copy from media/java/android/media/session/SessionCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsThreadEvent.aidl
index 9c0ae05..4a40d89 100644
--- a/media/java/android/media/session/SessionCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadEvent.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable SessionCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsThreadEvent;
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsThreadEvent.java
similarity index 66%
copy from media/java/android/media/session/ControllerCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsThreadEvent.java
index 788f5d3..e10baab 100644
--- a/media/java/android/media/session/ControllerCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadEvent.java
@@ -1,5 +1,5 @@
/*
- * Copyright 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.
@@ -13,6 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
+package android.telephony.ims;
-parcelable ControllerCallbackLink;
+import android.os.Parcelable;
+
+/**
+ * An event that happened on an {@link RcsThread}.
+ * @hide - TODO(sahinc) make this public
+ */
+public abstract class RcsThreadEvent implements Parcelable {
+}
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
similarity index 74%
rename from media/java/android/media/session/ControllerCallbackLink.aidl
rename to telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
index 788f5d3..82d985d 100644
--- a/media/java/android/media/session/ControllerCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable ControllerCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsThreadIconChangedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java
new file mode 100644
index 0000000..b308fef
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java
@@ -0,0 +1,49 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * An event that indicates an {@link RcsGroupThread}'s icon was changed.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsThreadIconChangedEvent extends RcsThreadEvent {
+ public static final Creator<RcsThreadIconChangedEvent> CREATOR =
+ new Creator<RcsThreadIconChangedEvent>() {
+ @Override
+ public RcsThreadIconChangedEvent createFromParcel(Parcel in) {
+ return new RcsThreadIconChangedEvent(in);
+ }
+
+ @Override
+ public RcsThreadIconChangedEvent[] newArray(int size) {
+ return new RcsThreadIconChangedEvent[size];
+ }
+ };
+
+ protected RcsThreadIconChangedEvent(Parcel in) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+}
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl
similarity index 74%
copy from media/java/android/media/session/ControllerCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl
index 788f5d3..54a311d 100644
--- a/media/java/android/media/session/ControllerCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable ControllerCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsThreadNameChangedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java
new file mode 100644
index 0000000..6f5cfdf
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java
@@ -0,0 +1,49 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * An event that indicates an {@link RcsGroupThread}'s name was changed.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsThreadNameChangedEvent extends RcsThreadEvent {
+ public static final Creator<RcsThreadNameChangedEvent> CREATOR =
+ new Creator<RcsThreadNameChangedEvent>() {
+ @Override
+ public RcsThreadNameChangedEvent createFromParcel(Parcel in) {
+ return new RcsThreadNameChangedEvent(in);
+ }
+
+ @Override
+ public RcsThreadNameChangedEvent[] newArray(int size) {
+ return new RcsThreadNameChangedEvent[size];
+ }
+ };
+
+ protected RcsThreadNameChangedEvent(Parcel in) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+}
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl
similarity index 73%
copy from media/java/android/media/session/ControllerCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl
index 788f5d3..047a424 100644
--- a/media/java/android/media/session/ControllerCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable ControllerCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsThreadParticipantJoinedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java
new file mode 100644
index 0000000..5c4073c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java
@@ -0,0 +1,49 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * An event that indicates an RCS participant has joined an {@link RcsGroupThread}.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsThreadParticipantJoinedEvent extends RcsThreadEvent {
+ public static final Creator<RcsThreadParticipantJoinedEvent> CREATOR =
+ new Creator<RcsThreadParticipantJoinedEvent>() {
+ @Override
+ public RcsThreadParticipantJoinedEvent createFromParcel(Parcel in) {
+ return new RcsThreadParticipantJoinedEvent(in);
+ }
+
+ @Override
+ public RcsThreadParticipantJoinedEvent[] newArray(int size) {
+ return new RcsThreadParticipantJoinedEvent[size];
+ }
+ };
+
+ protected RcsThreadParticipantJoinedEvent(Parcel in) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+}
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl
similarity index 73%
copy from media/java/android/media/session/ControllerCallbackLink.aidl
copy to telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl
index 788f5d3..52f9bbd 100644
--- a/media/java/android/media/session/ControllerCallbackLink.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ * 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,
@@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
-parcelable ControllerCallbackLink;
+package android.telephony.ims;
+
+parcelable RcsThreadParticipantLeftEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java
new file mode 100644
index 0000000..4bf86b9
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java
@@ -0,0 +1,49 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * An event that indicates an RCS participant has left an {@link RcsGroupThread}.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsThreadParticipantLeftEvent extends RcsThreadEvent {
+ public static final Creator<RcsThreadParticipantLeftEvent> CREATOR =
+ new Creator<RcsThreadParticipantLeftEvent>() {
+ @Override
+ public RcsThreadParticipantLeftEvent createFromParcel(Parcel in) {
+ return new RcsThreadParticipantLeftEvent(in);
+ }
+
+ @Override
+ public RcsThreadParticipantLeftEvent[] newArray(int size) {
+ return new RcsThreadParticipantLeftEvent[size];
+ }
+ };
+
+ protected RcsThreadParticipantLeftEvent(Parcel in) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+}
diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk
index f2bd407..34aa258 100644
--- a/tests/RollbackTest/Android.mk
+++ b/tests/RollbackTest/Android.mk
@@ -14,27 +14,49 @@
LOCAL_PATH:= $(call my-dir)
-# RollbackTestAppV1
+# RollbackTestAppAv1.apk
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_MANIFEST_FILE := TestApp/AndroidManifestV1.xml
-LOCAL_PACKAGE_NAME := RollbackTestAppV1
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/Av1.xml
+LOCAL_PACKAGE_NAME := RollbackTestAppAv1
include $(BUILD_PACKAGE)
-ROLLBACK_TEST_APP_V1 := $(LOCAL_INSTALLED_MODULE)
+ROLLBACK_TEST_APP_AV1 := $(LOCAL_INSTALLED_MODULE)
-# RollbackTestAppV2
+# RollbackTestAppAv2.apk
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_MANIFEST_FILE := TestApp/AndroidManifestV2.xml
-LOCAL_PACKAGE_NAME := RollbackTestAppV2
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/Av2.xml
+LOCAL_PACKAGE_NAME := RollbackTestAppAv2
include $(BUILD_PACKAGE)
-ROLLBACK_TEST_APP_V2 := $(LOCAL_INSTALLED_MODULE)
+ROLLBACK_TEST_APP_AV2 := $(LOCAL_INSTALLED_MODULE)
+
+# RollbackTestAppBv1.apk
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/Bv1.xml
+LOCAL_PACKAGE_NAME := RollbackTestAppBv1
+include $(BUILD_PACKAGE)
+ROLLBACK_TEST_APP_BV1 := $(LOCAL_INSTALLED_MODULE)
+
+# RollbackTestAppBv2.apk
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/Bv2.xml
+LOCAL_PACKAGE_NAME := RollbackTestAppBv2
+include $(BUILD_PACKAGE)
+ROLLBACK_TEST_APP_BV2 := $(LOCAL_INSTALLED_MODULE)
# RollbackTest
include $(CLEAR_VARS)
@@ -43,12 +65,17 @@
LOCAL_MODULE_TAGS := tests
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_COMPATIBILITY_SUITE := general-tests
-LOCAL_CERTIFICATE := platform
-LOCAL_JAVA_RESOURCE_FILES := $(ROLLBACK_TEST_APP_V1) $(ROLLBACK_TEST_APP_V2)
-LOCAL_SDK_VERSION := test_current
+LOCAL_JAVA_RESOURCE_FILES := \
+ $(ROLLBACK_TEST_APP_AV1) \
+ $(ROLLBACK_TEST_APP_AV2) \
+ $(ROLLBACK_TEST_APP_BV1) \
+ $(ROLLBACK_TEST_APP_BV2)
+LOCAL_SDK_VERSION := system_current
LOCAL_TEST_CONFIG := RollbackTest.xml
include $(BUILD_PACKAGE)
# Clean up local variables
-ROLLBACK_TEST_APP_V1 :=
-ROLLBACK_TEST_APP_V2 :=
+ROLLBACK_TEST_APP_AV1 :=
+ROLLBACK_TEST_APP_AV2 :=
+ROLLBACK_TEST_APP_BV1 :=
+ROLLBACK_TEST_APP_BV2 :=
diff --git a/tests/RollbackTest/AndroidManifest.xml b/tests/RollbackTest/AndroidManifest.xml
index d1535a3..e57a768 100644
--- a/tests/RollbackTest/AndroidManifest.xml
+++ b/tests/RollbackTest/AndroidManifest.xml
@@ -17,9 +17,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.tests.rollback" >
- <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
- <uses-permission android:name="android.permission.DELETE_PACKAGES" />
-
<application>
<receiver android:name="com.android.tests.rollback.LocalIntentSender"
android:exported="true" />
diff --git a/tests/RollbackTest/TestApp/AndroidManifestV1.xml b/tests/RollbackTest/TestApp/Av1.xml
similarity index 79%
copy from tests/RollbackTest/TestApp/AndroidManifestV1.xml
copy to tests/RollbackTest/TestApp/Av1.xml
index 45e9584..996d831 100644
--- a/tests/RollbackTest/TestApp/AndroidManifestV1.xml
+++ b/tests/RollbackTest/TestApp/Av1.xml
@@ -15,14 +15,17 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.tests.rollback.testapp"
+ package="com.android.tests.rollback.testapp.A"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="19" />
- <application android:label="Rollback Test App V1">
+ <application android:label="Rollback Test App A v1">
+ <meta-data android:name="version" android:value="1" />
+ <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" />
diff --git a/tests/RollbackTest/TestApp/AndroidManifestV2.xml b/tests/RollbackTest/TestApp/Av2.xml
similarity index 79%
copy from tests/RollbackTest/TestApp/AndroidManifestV2.xml
copy to tests/RollbackTest/TestApp/Av2.xml
index 0104086..21c7260 100644
--- a/tests/RollbackTest/TestApp/AndroidManifestV2.xml
+++ b/tests/RollbackTest/TestApp/Av2.xml
@@ -15,14 +15,17 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.tests.rollback.testapp"
+ package="com.android.tests.rollback.testapp.A"
android:versionCode="2"
android:versionName="2.0" >
<uses-sdk android:minSdkVersion="19" />
- <application android:label="Rollback Test App V2">
+ <application android:label="Rollback Test App A v2">
+ <meta-data android:name="version" android:value="2" />
+ <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" />
diff --git a/tests/RollbackTest/TestApp/AndroidManifestV1.xml b/tests/RollbackTest/TestApp/Bv1.xml
similarity index 79%
rename from tests/RollbackTest/TestApp/AndroidManifestV1.xml
rename to tests/RollbackTest/TestApp/Bv1.xml
index 45e9584..de0fd0d 100644
--- a/tests/RollbackTest/TestApp/AndroidManifestV1.xml
+++ b/tests/RollbackTest/TestApp/Bv1.xml
@@ -15,14 +15,17 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.tests.rollback.testapp"
+ package="com.android.tests.rollback.testapp.B"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="19" />
- <application android:label="Rollback Test App V1">
+ <application android:label="Rollback Test App B v1">
+ <meta-data android:name="version" android:value="1" />
+ <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" />
diff --git a/tests/RollbackTest/TestApp/AndroidManifestV2.xml b/tests/RollbackTest/TestApp/Bv2.xml
similarity index 79%
rename from tests/RollbackTest/TestApp/AndroidManifestV2.xml
rename to tests/RollbackTest/TestApp/Bv2.xml
index 0104086..6c2e66a 100644
--- a/tests/RollbackTest/TestApp/AndroidManifestV2.xml
+++ b/tests/RollbackTest/TestApp/Bv2.xml
@@ -15,14 +15,17 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.tests.rollback.testapp"
+ package="com.android.tests.rollback.testapp.B"
android:versionCode="2"
android:versionName="2.0" >
<uses-sdk android:minSdkVersion="19" />
- <application android:label="Rollback Test App V2">
+ <application android:label="Rollback Test App B v2">
+ <meta-data android:name="version" android:value="2" />
+ <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" />
diff --git a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/MainActivity.java b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/MainActivity.java
index 1856bac..9f1a060 100644
--- a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/MainActivity.java
+++ b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/MainActivity.java
@@ -27,5 +27,11 @@
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ try {
+ new ProcessUserData().processUserData(this);
+ } catch (ProcessUserData.UserDataException e) {
+ throw new AssertionError("Failed to process app user data", e);
+ }
}
}
diff --git a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java
new file mode 100644
index 0000000..fde6a83
--- /dev/null
+++ b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.rollback.testapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Scanner;
+
+/**
+ * A broadcast reciever to check for and update user app data version
+ * compatibility.
+ */
+public class ProcessUserData extends BroadcastReceiver {
+
+ /**
+ * Exception thrown in case of issue with user data.
+ */
+ public static class UserDataException extends Exception {
+ public UserDataException(String message) {
+ super(message);
+ }
+
+ public UserDataException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ try {
+ processUserData(context);
+ setResultCode(1);
+ } catch (UserDataException e) {
+ setResultCode(0);
+ setResultData(e.getMessage());
+ }
+ }
+
+ /**
+ * Update the app's user data version to match the app version.
+ *
+ * @param context The application context.
+ * @throws UserDataException in case of problems with app user data.
+ */
+ public void processUserData(Context context) throws UserDataException {
+ int appVersion = 0;
+ try {
+ ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
+ context.getPackageName(), PackageManager.GET_META_DATA);
+ Bundle bundle = appInfo.metaData;
+ appVersion = bundle.getInt("version");
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new UserDataException("Unable to get app version info", e);
+ }
+
+ // Read the version of the app's user data and ensure it is compatible
+ // with our version of the application.
+ File versionFile = new File(context.getFilesDir(), "version.txt");
+ try {
+ Scanner s = new Scanner(versionFile);
+ int userDataVersion = s.nextInt();
+ s.close();
+
+ if (userDataVersion > appVersion) {
+ throw new UserDataException("User data is from version " + userDataVersion
+ + ", which is not compatible with this version " + appVersion
+ + " of the RollbackTestApp");
+ }
+ } catch (FileNotFoundException e) {
+ // No problem. This is a fresh install of the app or the user data
+ // has been wiped.
+ }
+
+ // Record the current version of the app in the user data.
+ try {
+ PrintWriter pw = new PrintWriter(versionFile);
+ pw.println(appVersion);
+ pw.close();
+ } catch (IOException e) {
+ throw new UserDataException("Unable to write user data.", e);
+ }
+ }
+}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 02c1ce2..0ccfb19 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -16,13 +16,17 @@
package com.android.tests.rollback;
+import android.Manifest;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.support.test.InstrumentationRegistry;
import android.util.Log;
@@ -34,10 +38,13 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
@@ -49,7 +56,8 @@
private static final String TAG = "RollbackTest";
- private static final String TEST_APP_PACKAGE_NAME = "com.android.tests.rollback.testapp";
+ private static final String TEST_APP_A = "com.android.tests.rollback.testapp.A";
+ private static final String TEST_APP_B = "com.android.tests.rollback.testapp.B";
/**
* Test basic rollbacks.
@@ -71,85 +79,94 @@
};
context.registerReceiver(enableRollbackReceiver, enableRollbackFilter);
- // Register a broadcast receiver for notification when the rollback is
- // done executing.
- RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver();
- RollbackManager rm = RollbackTestUtils.getRollbackManager();
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.MANAGE_ROLLBACKS);
- // Uninstall com.android.tests.rollback.testapp
- RollbackTestUtils.uninstall("com.android.tests.rollback.testapp");
- assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+ // Register a broadcast receiver for notification when the rollback is
+ // done executing.
+ RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver();
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
- // TODO: There is currently a race condition between when the app is
- // uninstalled and when rollback manager deletes the rollback. Fix it
- // so that's not the case!
- for (int i = 0; i < 5; ++i) {
- for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
- if (TEST_APP_PACKAGE_NAME.equals(info.targetPackage.packageName)) {
- Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect.");
- Thread.sleep(1000);
- break;
+ // Uninstall TEST_APP_A
+ RollbackTestUtils.uninstall(TEST_APP_A);
+ assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ // TODO: There is currently a race condition between when the app is
+ // uninstalled and when rollback manager deletes the rollback. Fix it
+ // so that's not the case!
+ for (int i = 0; i < 5; ++i) {
+ for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
+ if (TEST_APP_A.equals(info.targetPackage.packageName)) {
+ Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect.");
+ Thread.sleep(1000);
+ break;
+ }
}
}
- }
- // The app should not be available for rollback.
- assertNull(rm.getAvailableRollback(TEST_APP_PACKAGE_NAME));
- assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
+ // The app should not be available for rollback.
+ assertNull(rm.getAvailableRollback(TEST_APP_A));
+ assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
- // There should be no recently executed rollbacks for this package.
- for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
- assertNotEquals(TEST_APP_PACKAGE_NAME, info.targetPackage.packageName);
- }
-
- // Install v1 of the app (without rollbacks enabled).
- RollbackTestUtils.install("RollbackTestAppV1.apk", false);
- assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
-
- // Upgrade from v1 to v2, with rollbacks enabled.
- RollbackTestUtils.install("RollbackTestAppV2.apk", true);
- assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
-
- // The app should now be available for rollback.
- assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
- RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
- assertNotNull(rollback);
- assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
- assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
- assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
-
- // We should not have received any rollback requests yet.
- // TODO: Possibly flaky if, by chance, some other app on device
- // happens to be rolled back at the same time?
- assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
-
- // Roll back the app.
- RollbackTestUtils.rollback(rollback);
- assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
-
- // Verify we received a broadcast for the rollback.
- // TODO: Race condition between the timeout and when the broadcast is
- // received could lead to test flakiness.
- Intent broadcast = broadcastReceiver.poll(5, TimeUnit.SECONDS);
- assertNotNull(broadcast);
- assertEquals(TEST_APP_PACKAGE_NAME, broadcast.getData().getSchemeSpecificPart());
- assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
-
- // Verify the recent rollback has been recorded.
- rollback = null;
- for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
- if (TEST_APP_PACKAGE_NAME.equals(r.targetPackage.packageName)) {
- assertNull(rollback);
- rollback = r;
+ // There should be no recently executed rollbacks for this package.
+ for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
+ assertNotEquals(TEST_APP_A, info.targetPackage.packageName);
}
- }
- assertNotNull(rollback);
- assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
- assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
- assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
- broadcastReceiver.unregister();
- context.unregisterReceiver(enableRollbackReceiver);
+ // Install v1 of the app (without rollbacks enabled).
+ RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ // Upgrade from v1 to v2, with rollbacks enabled.
+ RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ // The app should now be available for rollback.
+ assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+ RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
+ assertNotNull(rollback);
+ assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+ assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+ assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+
+ // We should not have received any rollback requests yet.
+ // TODO: Possibly flaky if, by chance, some other app on device
+ // happens to be rolled back at the same time?
+ assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
+
+ // Roll back the app.
+ RollbackTestUtils.rollback(rollback);
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ // Verify we received a broadcast for the rollback.
+ // TODO: Race condition between the timeout and when the broadcast is
+ // received could lead to test flakiness.
+ Intent broadcast = broadcastReceiver.poll(5, TimeUnit.SECONDS);
+ assertNotNull(broadcast);
+ assertEquals(TEST_APP_A, broadcast.getData().getSchemeSpecificPart());
+ assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
+
+ // Verify the recent rollback has been recorded.
+ rollback = null;
+ for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
+ if (TEST_APP_A.equals(r.targetPackage.packageName)) {
+ assertNull(rollback);
+ rollback = r;
+ }
+ }
+ assertNotNull(rollback);
+ assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+ assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+ assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+
+ broadcastReceiver.unregister();
+ context.unregisterReceiver(enableRollbackReceiver);
+ } finally {
+ RollbackTestUtils.dropShellPermissionIdentity();
+ }
}
/**
@@ -157,65 +174,74 @@
*/
@Test
public void testRollbackDataPersistence() throws Exception {
- RollbackManager rm = RollbackTestUtils.getRollbackManager();
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.MANAGE_ROLLBACKS);
- // Prep installation of com.android.tests.rollback.testapp
- RollbackTestUtils.uninstall("com.android.tests.rollback.testapp");
- RollbackTestUtils.install("RollbackTestAppV1.apk", false);
- RollbackTestUtils.install("RollbackTestAppV2.apk", true);
- assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
- // The app should now be available for rollback.
- assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
- RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
- assertNotNull(rollback);
- assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
- assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
- assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+ // Prep installation of TEST_APP_A
+ RollbackTestUtils.uninstall(TEST_APP_A);
+ RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+ RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
- // Reload the persisted data.
- rm.reloadPersistedData();
+ // The app should now be available for rollback.
+ assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+ RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
+ assertNotNull(rollback);
+ assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+ assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+ assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
- // The app should still be available for rollback.
- assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
- rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
- assertNotNull(rollback);
- assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
- assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
- assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+ // Reload the persisted data.
+ rm.reloadPersistedData();
- // Roll back the app.
- RollbackTestUtils.rollback(rollback);
- assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+ // The app should still be available for rollback.
+ assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+ rollback = rm.getAvailableRollback(TEST_APP_A);
+ assertNotNull(rollback);
+ assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+ assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+ assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
- // Verify the recent rollback has been recorded.
- rollback = null;
- for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
- if (TEST_APP_PACKAGE_NAME.equals(r.targetPackage.packageName)) {
- assertNull(rollback);
- rollback = r;
+ // Roll back the app.
+ RollbackTestUtils.rollback(rollback);
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ // Verify the recent rollback has been recorded.
+ rollback = null;
+ for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
+ if (TEST_APP_A.equals(r.targetPackage.packageName)) {
+ assertNull(rollback);
+ rollback = r;
+ }
}
- }
- assertNotNull(rollback);
- assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
- assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
- assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+ assertNotNull(rollback);
+ assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+ assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+ assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
- // Reload the persisted data.
- rm.reloadPersistedData();
+ // Reload the persisted data.
+ rm.reloadPersistedData();
- // Verify the recent rollback is still recorded.
- rollback = null;
- for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
- if (TEST_APP_PACKAGE_NAME.equals(r.targetPackage.packageName)) {
- assertNull(rollback);
- rollback = r;
+ // Verify the recent rollback is still recorded.
+ rollback = null;
+ for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
+ if (TEST_APP_A.equals(r.targetPackage.packageName)) {
+ assertNull(rollback);
+ rollback = r;
+ }
}
+ assertNotNull(rollback);
+ assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+ assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+ assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+ } finally {
+ RollbackTestUtils.dropShellPermissionIdentity();
}
- assertNotNull(rollback);
- assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
- assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
- assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
}
/**
@@ -224,26 +250,108 @@
*/
@Test
public void testRollbackExpiration() throws Exception {
- RollbackManager rm = RollbackTestUtils.getRollbackManager();
- RollbackTestUtils.uninstall("com.android.tests.rollback.testapp");
- RollbackTestUtils.install("RollbackTestAppV1.apk", false);
- RollbackTestUtils.install("RollbackTestAppV2.apk", true);
- assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.MANAGE_ROLLBACKS);
- // The app should now be available for rollback.
- assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
- RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
- assertNotNull(rollback);
- assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
- assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
- assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+ 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));
- // Expire the rollback.
- rm.expireRollbackForPackage(TEST_APP_PACKAGE_NAME);
+ // The app should now be available for rollback.
+ assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+ RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
+ assertNotNull(rollback);
+ assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+ assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+ assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
- // The rollback should no longer be available.
- assertNull(rm.getAvailableRollback(TEST_APP_PACKAGE_NAME));
- assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
+ // Expire the rollback.
+ rm.expireRollbackForPackage(TEST_APP_A);
+
+ // The rollback should no longer be available.
+ assertNull(rm.getAvailableRollback(TEST_APP_A));
+ assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+ } finally {
+ RollbackTestUtils.dropShellPermissionIdentity();
+ }
+ }
+
+ private static final String NO_RESPONSE = "NO RESPONSE";
+
+ // Calls into the test app to process user data.
+ // Asserts if the user data could not be processed or was version
+ // incompatible with the previously processed user data.
+ private void processUserData(String packageName) throws Exception {
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(packageName,
+ "com.android.tests.rollback.testapp.ProcessUserData"));
+ Context context = InstrumentationRegistry.getContext();
+
+ HandlerThread handlerThread = new HandlerThread("RollbackTestHandlerThread");
+ handlerThread.start();
+
+ // It can sometimes take a while after rollback before the app will
+ // receive this broadcast, so try a few times in a loop.
+ String result = NO_RESPONSE;
+ for (int i = 0; result.equals(NO_RESPONSE) && i < 5; ++i) {
+ BlockingQueue<String> resultQueue = new LinkedBlockingQueue<>();
+ context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (getResultCode() == 1) {
+ resultQueue.add("OK");
+ } else {
+ // If the test app doesn't receive the broadcast or
+ // fails to set the result data, then getResultData
+ // here returns the initial NO_RESPONSE data passed to
+ // the sendOrderedBroadcast call.
+ resultQueue.add(getResultData());
+ }
+ }
+ }, new Handler(handlerThread.getLooper()), 0, NO_RESPONSE, null);
+
+ result = resultQueue.poll(10, TimeUnit.SECONDS);
+ if (result == null) {
+ result = "ProcessUserData broadcast timed out";
+ }
+ }
+
+ handlerThread.quit();
+ if (!"OK".equals(result)) {
+ fail(result);
+ }
+ }
+
+ /**
+ * Test that app user data is rolled back.
+ * TODO: Stop ignoring this test once user data rollback is supported.
+ */
+ @Ignore @Test
+ public void testUserDataRollback() throws Exception {
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.MANAGE_ROLLBACKS);
+
+ RollbackTestUtils.uninstall(TEST_APP_A);
+ RollbackTestUtils.install("RollbackTestAppV1.apk", false);
+ processUserData(TEST_APP_A);
+ RollbackTestUtils.install("RollbackTestAppV2.apk", true);
+ processUserData(TEST_APP_A);
+
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+ RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
+ RollbackTestUtils.rollback(rollback);
+ processUserData(TEST_APP_A);
+ } finally {
+ RollbackTestUtils.dropShellPermissionIdentity();
+ }
}
/**
@@ -270,4 +378,164 @@
// called, even when the test fails?
broadcastReceiver.unregister();
}
+
+ /**
+ * Regression test for rollback in the case when multiple apps are
+ * available for rollback at the same time.
+ */
+ @Test
+ public void testMultipleRollbackAvailable() throws Exception {
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.MANAGE_ROLLBACKS);
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+ // Prep installation of the test apps.
+ RollbackTestUtils.uninstall(TEST_APP_A);
+ RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+ RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ RollbackTestUtils.uninstall(TEST_APP_B);
+ RollbackTestUtils.install("RollbackTestAppBv1.apk", false);
+ RollbackTestUtils.install("RollbackTestAppBv2.apk", true);
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+ // Both test apps should now be available for rollback, and the
+ // targetPackage returned for rollback should be correct.
+ RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A);
+ assertNotNull(rollbackA);
+ assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName);
+
+ RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B);
+ assertNotNull(rollbackB);
+ assertEquals(TEST_APP_B, rollbackB.targetPackage.packageName);
+
+ // Executing rollback should roll back the correct package.
+ RollbackTestUtils.rollback(rollbackA);
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+ RollbackTestUtils.uninstall(TEST_APP_A);
+ RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+ RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ RollbackTestUtils.rollback(rollbackB);
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+ } finally {
+ RollbackTestUtils.dropShellPermissionIdentity();
+ }
+ }
+
+ /**
+ * Test that the MANAGE_ROLLBACKS permission is required to call
+ * RollbackManager APIs.
+ */
+ @Test
+ public void testManageRollbacksPermission() throws Exception {
+ // We shouldn't be allowed to call any of the RollbackManager APIs
+ // without the MANAGE_ROLLBACKS permission.
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+ try {
+ rm.getAvailableRollback(TEST_APP_A);
+ fail("expected SecurityException");
+ } catch (SecurityException e) {
+ // Expected.
+ }
+
+ try {
+ rm.getPackagesWithAvailableRollbacks();
+ fail("expected SecurityException");
+ } catch (SecurityException e) {
+ // Expected.
+ }
+
+ try {
+ rm.getRecentlyExecutedRollbacks();
+ fail("expected SecurityException");
+ } catch (SecurityException e) {
+ // Expected.
+ }
+
+ try {
+ // TODO: What if the implementation checks arguments for non-null
+ // first? Then this test isn't valid.
+ rm.executeRollback(null, null);
+ fail("expected SecurityException");
+ } catch (SecurityException e) {
+ // Expected.
+ }
+
+ try {
+ rm.reloadPersistedData();
+ fail("expected SecurityException");
+ } catch (SecurityException e) {
+ // Expected.
+ }
+
+ try {
+ rm.expireRollbackForPackage(TEST_APP_A);
+ fail("expected SecurityException");
+ } catch (SecurityException e) {
+ // Expected.
+ }
+ }
+
+ /**
+ * Test rollback of multi-package installs.
+ * TODO: Stop ignoring this test once support for multi-package rollback
+ * is implemented.
+ */
+ @Ignore @Test
+ public void testMultiPackage() throws Exception {
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.MANAGE_ROLLBACKS);
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+ // Prep installation of the test apps.
+ RollbackTestUtils.uninstall(TEST_APP_A);
+ RollbackTestUtils.uninstall(TEST_APP_B);
+ RollbackTestUtils.installMultiPackage(false,
+ "RollbackTestAppAv1.apk",
+ "RollbackTestAppBv1.apk");
+ RollbackTestUtils.installMultiPackage(true,
+ "RollbackTestAppAv2.apk",
+ "RollbackTestAppBv2.apk");
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+ // TEST_APP_A should now be available for rollback.
+ assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+ RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
+ assertNotNull(rollback);
+
+ // TODO: Test the dependent apps for rollback are correct once we
+ // support that in the RollbackInfo API.
+
+ // Rollback the app. It should cause both test apps to be rolled
+ // back.
+ RollbackTestUtils.rollback(rollback);
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+ // We should not see a recent rollback listed for TEST_APP_B
+ for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
+ assertNotEquals(TEST_APP_B, r.targetPackage.packageName);
+ }
+
+ // TODO: Test the listed dependent apps for the recently executed
+ // rollback are correct once we support that in the RollbackInfo
+ // API.
+ } finally {
+ RollbackTestUtils.dropShellPermissionIdentity();
+ }
+ }
}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
index c5ce3fa..fbc3d8f 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
@@ -135,15 +135,64 @@
assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
}
- // TODO: Unused
- static void adoptShellPermissionIdentity() {
+ /**
+ * Installs the apks with the given resource names as an atomic set.
+ *
+ * @param enableRollback if rollback should be enabled.
+ * @param resourceNames names of the class loader resource for the apks to
+ * install.
+ * @throws AssertionError if the installation fails.
+ */
+ static void installMultiPackage(boolean enableRollback, String... resourceNames)
+ throws InterruptedException, IOException {
+ Context context = InstrumentationRegistry.getContext();
+ PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
+
+ PackageInstaller.SessionParams multiPackageParams = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ multiPackageParams.setMultiPackage();
+ if (enableRollback) {
+ // TODO: Do we set this on the parent params, the child params, or
+ // both?
+ multiPackageParams.setEnableRollback();
+ }
+ int multiPackageId = packageInstaller.createSession(multiPackageParams);
+ PackageInstaller.Session multiPackage = packageInstaller.openSession(multiPackageId);
+
+ for (String resourceName : resourceNames) {
+ PackageInstaller.Session session = null;
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ if (enableRollback) {
+ params.setEnableRollback();
+ }
+ int sessionId = packageInstaller.createSession(params);
+ session = packageInstaller.openSession(sessionId);
+
+ ClassLoader loader = RollbackTest.class.getClassLoader();
+ try (OutputStream packageInSession = session.openWrite("package", 0, -1);
+ InputStream is = loader.getResourceAsStream(resourceName);) {
+ byte[] buffer = new byte[4096];
+ int n;
+ while ((n = is.read(buffer)) >= 0) {
+ packageInSession.write(buffer, 0, n);
+ }
+ }
+ multiPackage.addChildSessionId(sessionId);
+ }
+
+ // Commit the session (this will start the installation workflow).
+ multiPackage.commit(LocalIntentSender.getIntentSender());
+ assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
+ }
+
+ static void adoptShellPermissionIdentity(String... permissions) {
InstrumentationRegistry
.getInstrumentation()
.getUiAutomation()
- .adoptShellPermissionIdentity();
+ .adoptShellPermissionIdentity(permissions);
}
- // TODO: Unused
static void dropShellPermissionIdentity() {
InstrumentationRegistry
.getInstrumentation()
diff --git a/tools/aapt2/cmd/Command.cpp b/tools/aapt2/cmd/Command.cpp
index bdee5c9..4424a35 100644
--- a/tools/aapt2/cmd/Command.cpp
+++ b/tools/aapt2/cmd/Command.cpp
@@ -21,80 +21,97 @@
#include <string>
#include <vector>
+#include "android-base/stringprintf.h"
+#include "android-base/utf8.h"
#include "androidfw/StringPiece.h"
#include "util/Util.h"
+using android::base::StringPrintf;
using android::StringPiece;
namespace aapt {
-void Command::AddRequiredFlag(const StringPiece& name,
- const StringPiece& description, std::string* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- *value = arg.to_string();
+std::string GetSafePath(const StringPiece& arg) {
+#ifdef _WIN32
+ // If the path exceeds the maximum path length for Windows, encode the path using the
+ // extended-length prefix
+ std::wstring path16;
+ CHECK(android::base::UTF8PathToWindowsLongPath(arg.data(), &path16))
+ << "Failed to convert file path to UTF-16: file path " << arg.data();
+
+ std::string path8;
+ CHECK(android::base::WideToUTF8(path16, &path8))
+ << "Failed to convert file path back to UTF-8: file path " << arg.data();
+
+ return path8;
+#else
+ return arg.to_string();
+#endif
+}
+
+void Command::AddRequiredFlag(const StringPiece& name, const StringPiece& description,
+ std::string* value, uint32_t flags) {
+ auto func = [value, flags](const StringPiece& arg) -> bool {
+ *value = (flags & Command::kPath) ? GetSafePath(arg) : arg.to_string();
return true;
};
- flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
+ flags_.emplace_back(Flag(name, description, /* required */ true, /* num_args */ 1, func));
}
-void Command::AddRequiredFlagList(const StringPiece& name,
- const StringPiece& description,
- std::vector<std::string>* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- value->push_back(arg.to_string());
+void Command::AddRequiredFlagList(const StringPiece& name, const StringPiece& description,
+ std::vector<std::string>* value, uint32_t flags) {
+ auto func = [value, flags](const StringPiece& arg) -> bool {
+ value->push_back((flags & Command::kPath) ? GetSafePath(arg) : arg.to_string());
return true;
};
- flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
+ flags_.emplace_back(Flag(name, description, /* required */ true, /* num_args */ 1, func));
}
-void Command::AddOptionalFlag(const StringPiece& name,
- const StringPiece& description,
- Maybe<std::string>* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- *value = arg.to_string();
+void Command::AddOptionalFlag(const StringPiece& name, const StringPiece& description,
+ Maybe<std::string>* value, uint32_t flags) {
+ auto func = [value, flags](const StringPiece& arg) -> bool {
+ *value = (flags & Command::kPath) ? GetSafePath(arg) : arg.to_string();
return true;
};
- flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
+ flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func));
}
-void Command::AddOptionalFlagList(const StringPiece& name,
- const StringPiece& description,
- std::vector<std::string>* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- value->push_back(arg.to_string());
+void Command::AddOptionalFlagList(const StringPiece& name, const StringPiece& description,
+ std::vector<std::string>* value, uint32_t flags) {
+ auto func = [value, flags](const StringPiece& arg) -> bool {
+ value->push_back((flags & Command::kPath) ? GetSafePath(arg) : arg.to_string());
return true;
};
- flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
+ flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func));
}
-void Command::AddOptionalFlagList(const StringPiece& name,
- const StringPiece& description,
- std::unordered_set<std::string>* value) {
+void Command::AddOptionalFlagList(const StringPiece& name, const StringPiece& description,
+ std::unordered_set<std::string>* value) {
auto func = [value](const StringPiece& arg) -> bool {
value->insert(arg.to_string());
return true;
};
- flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
+ flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func));
}
-void Command::AddOptionalSwitch(const StringPiece& name,
- const StringPiece& description, bool* value) {
+void Command::AddOptionalSwitch(const StringPiece& name, const StringPiece& description,
+ bool* value) {
auto func = [value](const StringPiece& arg) -> bool {
*value = true;
return true;
};
- flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 0, false});
+ flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 0, func));
}
void Command::AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand, bool experimental) {
- subcommand->fullname_ = name_ + " " + subcommand->name_;
+ subcommand->full_subcommand_name_ = StringPrintf("%s %s", name_.data(), subcommand->name_.data());
if (experimental) {
experimental_subcommands_.push_back(std::move(subcommand));
} else {
@@ -102,14 +119,14 @@
}
}
-void Command::SetDescription(const android::StringPiece& description) {
+void Command::SetDescription(const StringPiece& description) {
description_ = description.to_string();
}
void Command::Usage(std::ostream* out) {
constexpr size_t kWidth = 50;
- *out << fullname_;
+ *out << full_subcommand_name_;
if (!subcommands_.empty()) {
*out << " [subcommand]";
@@ -117,7 +134,7 @@
*out << " [options]";
for (const Flag& flag : flags_) {
- if (flag.required) {
+ if (flag.is_required) {
*out << " " << flag.name << " arg";
}
}
@@ -160,29 +177,31 @@
out->flush();
}
-int Command::Execute(const std::vector<android::StringPiece>& args, std::ostream* out_error) {
+int Command::Execute(const std::vector<StringPiece>& args, std::ostream* out_error) {
std::vector<std::string> file_args;
for (size_t i = 0; i < args.size(); i++) {
- StringPiece arg = args[i];
+ const StringPiece& arg = args[i];
if (*(arg.data()) != '-') {
// Continue parsing as the subcommand if the first argument matches one of the subcommands
if (i == 0) {
for (auto& subcommand : subcommands_) {
- if (arg == subcommand->name_ || arg==subcommand->short_name_) {
+ if (arg == subcommand->name_ || (!subcommand->short_name_.empty()
+ && arg == subcommand->short_name_)) {
return subcommand->Execute(
- std::vector<android::StringPiece>(args.begin() + 1, args.end()), out_error);
+ std::vector<StringPiece>(args.begin() + 1, args.end()), out_error);
}
}
for (auto& subcommand : experimental_subcommands_) {
- if (arg == subcommand->name_ || arg==subcommand->short_name_) {
+ if (arg == subcommand->name_ || (!subcommand->short_name_.empty()
+ && arg == subcommand->short_name_)) {
return subcommand->Execute(
- std::vector<android::StringPiece>(args.begin() + 1, args.end()), out_error);
+ std::vector<StringPiece>(args.begin() + 1, args.end()), out_error);
}
}
}
- file_args.push_back(arg.to_string());
+ file_args.push_back(GetSafePath(arg));
continue;
}
@@ -205,7 +224,7 @@
} else {
flag.action({});
}
- flag.parsed = true;
+ flag.found = true;
match = true;
break;
}
@@ -219,7 +238,7 @@
}
for (const Flag& flag : flags_) {
- if (flag.required && !flag.parsed) {
+ if (flag.is_required && !flag.found) {
*out_error << "missing required flag " << flag.name << "\n\n";
Usage(out_error);
return 1;
diff --git a/tools/aapt2/cmd/Command.h b/tools/aapt2/cmd/Command.h
index 1694988..d21571d 100644
--- a/tools/aapt2/cmd/Command.h
+++ b/tools/aapt2/cmd/Command.h
@@ -32,55 +32,83 @@
class Command {
public:
explicit Command(const android::StringPiece& name) : name_(name.to_string()),
- fullname_(name.to_string()) { }
+ short_name_(""),
+ full_subcommand_name_(name.to_string()) {}
+
explicit Command(const android::StringPiece& name, const android::StringPiece& short_name)
- : name_(name.to_string()), short_name_(short_name.to_string()), fullname_(name.to_string()) {}
+ : name_(name.to_string()), short_name_(short_name.to_string()),
+ full_subcommand_name_(name.to_string()) {}
+
virtual ~Command() = default;
+ // Behavior flags used with the following functions that change how the command flags are parsed
+ // displayed.
+ enum : uint32_t {
+ // Indicates the arguments are file or folder paths. On Windows, paths that exceed the maximum
+ // path length will be converted to use the extended path prefix '//?/'. Without this
+ // conversion, files with long paths cannot be opened.
+ kPath = 1 << 0,
+ };
+
void AddRequiredFlag(const android::StringPiece& name, const android::StringPiece& description,
- std::string* value);
- void AddRequiredFlagList(const android::StringPiece& name, const android::StringPiece&
- description, std::vector<std::string>* value);
+ std::string* value, uint32_t flags = 0);
+
+ void AddRequiredFlagList(const android::StringPiece& name,
+ const android::StringPiece& description, std::vector<std::string>* value,
+ uint32_t flags = 0);
+
void AddOptionalFlag(const android::StringPiece& name, const android::StringPiece& description,
- Maybe<std::string>* value);
+ Maybe<std::string>* value, uint32_t flags = 0);
+
void AddOptionalFlagList(const android::StringPiece& name,
- const android::StringPiece& description, std::vector<std::string>* value);
+ const android::StringPiece& description, std::vector<std::string>* value,
+ uint32_t flags = 0);
+
void AddOptionalFlagList(const android::StringPiece& name,
- const android::StringPiece& description, std::unordered_set<std::string>* value);
+ const android::StringPiece& description,
+ std::unordered_set<std::string>* value);
+
void AddOptionalSwitch(const android::StringPiece& name, const android::StringPiece& description,
- bool* value);
+ bool* value);
+
void AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand, bool experimental = false);
void SetDescription(const android::StringPiece& name);
- /** Prints the help menu of the command. */
+ // Prints the help menu of the command.
void Usage(std::ostream* out);
- /**
- * Parses the command line arguments, sets the flag variable values, and runs the action of
- * the command. If the arguments fail to parse to the command and its subcommands, then the action
- * will not be run and the usage will be printed instead.
- **/
+ // Parses the command line arguments, sets the flag variable values, and runs the action of
+ // the command. If the arguments fail to parse to the command and its subcommands, then the action
+ // will not be run and the usage will be printed instead.
int Execute(const std::vector<android::StringPiece>& args, std::ostream* outError);
- /** The action to preform when the command is executed. */
+ // The action to preform when the command is executed.
virtual int Action(const std::vector<std::string>& args) = 0;
private:
- struct Flag {
- std::string name;
- std::string description;
- std::function<bool(const android::StringPiece& value)> action;
- bool required;
- size_t num_args;
+ DISALLOW_COPY_AND_ASSIGN(Command);
- bool parsed;
+ struct Flag {
+ explicit Flag(const android::StringPiece& name, const android::StringPiece& description,
+ const bool is_required, const size_t num_args,
+ std::function<bool(const android::StringPiece& value)>&& action)
+ : name(name.to_string()), description(description.to_string()), is_required(is_required),
+ num_args(num_args), action(std::move(action)) {}
+
+ const std::string name;
+ const std::string description;
+ const bool is_required;
+ const size_t num_args;
+ const std::function<bool(const android::StringPiece& value)> action;
+ bool found = false;
};
- std::string description_;
- std::string name_;
- std::string short_name_;
- std::string fullname_;
+ const std::string name_;
+ const std::string short_name_;
+ std::string description_ = "";
+ std::string full_subcommand_name_;
+
std::vector<Flag> flags_;
std::vector<std::unique_ptr<Command>> subcommands_;
std::vector<std::unique_ptr<Command>> experimental_subcommands_;
diff --git a/tools/aapt2/cmd/Command_test.cpp b/tools/aapt2/cmd/Command_test.cpp
new file mode 100644
index 0000000..65608fd
--- /dev/null
+++ b/tools/aapt2/cmd/Command_test.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#include "Command.h"
+
+#include "test/Test.h"
+
+using ::testing::Eq;
+
+namespace aapt {
+
+class TestCommand : public Command {
+ public:
+ explicit TestCommand() : Command("command") {}
+ int Action(const std::vector<std::string>& args) override {
+ args_ = args;
+ return 0;
+ }
+
+ std::vector<std::string> args_;
+};
+
+#ifdef _WIN32
+TEST(CommandTest, LongFullyQualifiedPathWindows) {
+ TestCommand command;
+ std::string required_flag;
+ command.AddRequiredFlag("--rflag", "", &required_flag, Command::kPath);
+ Maybe<std::string> optional_flag;
+ command.AddOptionalFlag("--oflag", "", &optional_flag, Command::kPath);
+ std::vector<std::string> required_flag_list;
+ command.AddRequiredFlagList("--rlflag", "", &required_flag_list, Command::kPath);
+ std::vector<std::string> optional_flag_list;
+ command.AddOptionalFlagList("--olflag", "", &optional_flag_list, Command::kPath);
+ std::string non_path_flag;
+ command.AddRequiredFlag("--nflag", "", &non_path_flag);
+
+ const std::string kLongPath =
+ "C:\\Users\\jedo\\_bazel_jedo\\vcmdctjv\\execroot\\__main__\\_tmp"
+ "\\6767b4778f8798efc0f784ee74fa70ee\\tests\\testApksAr8c7560a9a65"
+ "\\1346ee7c014a089fb55d8c46cf3d9\\project\\baseModule\\build"
+ "\\intermediates\\processed_res\\minified\\processMinifiedResources"
+ "\\1346ee7c014a089fb55d8c46cf3d9\\project\\baseModule\\build"
+ "\\intermediates\\processed_res\\minified\\processMinifiedResources"
+ "\\out\\resources-minified.ap_";
+
+ const std::string kExpected =
+ "\\\\?\\C:\\Users\\jedo\\_bazel_jedo\\vcmdctjv\\execroot\\__main__\\_tmp"
+ "\\6767b4778f8798efc0f784ee74fa70ee\\tests\\testApksAr8c7560a9a65"
+ "\\1346ee7c014a089fb55d8c46cf3d9\\project\\baseModule\\build"
+ "\\intermediates\\processed_res\\minified\\processMinifiedResources"
+ "\\1346ee7c014a089fb55d8c46cf3d9\\project\\baseModule\\build"
+ "\\intermediates\\processed_res\\minified\\processMinifiedResources"
+ "\\out\\resources-minified.ap_";
+
+
+ ASSERT_THAT(command.Execute({"--rflag", kLongPath,
+ "--oflag", kLongPath,
+ "--rlflag", kLongPath,
+ "--rlflag", kLongPath,
+ "--olflag", kLongPath,
+ "--olflag", kLongPath,
+ "--nflag", kLongPath,
+ kLongPath, kLongPath}, &std::cerr), Eq(0));
+
+ ASSERT_THAT(required_flag, Eq(kExpected));
+ ASSERT_THAT(optional_flag, Eq(kExpected));
+ ASSERT_THAT(required_flag_list.size(), Eq(2));
+ ASSERT_THAT(required_flag_list[0], Eq(kExpected));
+ ASSERT_THAT(required_flag_list[1], Eq(kExpected));
+ ASSERT_THAT(optional_flag_list.size(), Eq(2));
+ ASSERT_THAT(optional_flag_list[0], Eq(kExpected));
+ ASSERT_THAT(optional_flag_list[1], Eq(kExpected));
+
+ // File arguments should also be converted to use the long path prefix
+ ASSERT_THAT(command.args_.size(), Eq(2));
+ ASSERT_THAT(command.args_[0], Eq(kExpected));
+ ASSERT_THAT(command.args_[1], Eq(kExpected));
+
+ // Do not convert flags that are not marged as paths
+ ASSERT_THAT(non_path_flag, Eq(kLongPath));
+}
+#endif
+
+} // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index c429d5f..9b32cb3 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -44,13 +44,13 @@
explicit CompileCommand(IDiagnostics* diagnostic) : Command("compile", "c"),
diagnostic_(diagnostic) {
SetDescription("Compiles resources to be linked into an apk.");
- AddRequiredFlag("-o", "Output path", &options_.output_path);
- AddOptionalFlag("--dir", "Directory to scan for resources", &options_.res_dir);
+ AddRequiredFlag("-o", "Output path", &options_.output_path, Command::kPath);
+ AddOptionalFlag("--dir", "Directory to scan for resources", &options_.res_dir, Command::kPath);
AddOptionalFlag("--zip", "Zip file containing the res directory to scan for resources",
- &options_.res_zip);
+ &options_.res_zip, Command::kPath);
AddOptionalFlag("--output-text-symbols",
"Generates a text file containing the resource symbols in the\n"
- "specified file", &options_.generate_text_symbols_path);
+ "specified file", &options_.generate_text_symbols_path, Command::kPath);
AddOptionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales "
"(en-XA and ar-XB)", &options_.pseudolocalize);
AddOptionalSwitch("--no-crunch", "Disables PNG processing", &options_.no_png_crunch);
@@ -70,8 +70,9 @@
Maybe<std::string> visibility_;
};
-int Compile(IAaptContext* context, io::IFileCollection* inputs,
- IArchiveWriter* output_writer, CompileOptions& options);
+int Compile(IAaptContext* context, io::IFileCollection* inputs, IArchiveWriter* output_writer,
+ CompileOptions& options);
+
}// namespace aapt
#endif //AAPT2_COMPILE_H
diff --git a/tools/aapt2/cmd/Convert.h b/tools/aapt2/cmd/Convert.h
index 6a6719c..14016b1 100644
--- a/tools/aapt2/cmd/Convert.h
+++ b/tools/aapt2/cmd/Convert.h
@@ -27,7 +27,7 @@
public:
explicit ConvertCommand() : Command("convert") {
SetDescription("Converts an apk between binary and proto formats.");
- AddRequiredFlag("-o", "Output path", &output_path_);
+ AddRequiredFlag("-o", "Output path", &output_path_, Command::kPath);
AddOptionalFlag("--output-format", android::base::StringPrintf("Format of the output. "
"Accepted values are '%s' and '%s'. When not set, defaults to '%s'.",
kOutputFormatProto, kOutputFormatBinary, kOutputFormatBinary), &output_format_);
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 950dac20..f740d53 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -100,24 +100,26 @@
explicit LinkCommand(IDiagnostics* diag) : Command("link", "l"),
diag_(diag) {
SetDescription("Links resources into an apk.");
- AddRequiredFlag("-o", "Output path.", &options_.output_path);
+ AddRequiredFlag("-o", "Output path.", &options_.output_path, Command::kPath);
AddRequiredFlag("--manifest", "Path to the Android manifest to build.",
- &options_.manifest_path);
- AddOptionalFlagList("-I", "Adds an Android APK to link against.", &options_.include_paths);
+ &options_.manifest_path, Command::kPath);
+ AddOptionalFlagList("-I", "Adds an Android APK to link against.", &options_.include_paths,
+ Command::kPath);
AddOptionalFlagList("-A", "An assets directory to include in the APK. These are unprocessed.",
- &options_.assets_dirs);
+ &options_.assets_dirs, Command::kPath);
AddOptionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
- "The last conflicting resource given takes precedence.", &overlay_arg_list_);
+ "The last conflicting resource given takes precedence.", &overlay_arg_list_,
+ Command::kPath);
AddOptionalFlag("--package-id",
"Specify the package ID to use for this app. Must be greater or equal to\n"
"0x7f and can't be used with --static-lib or --shared-lib.", &package_id_);
AddOptionalFlag("--java", "Directory in which to generate R.java.",
- &options_.generate_java_class_path);
+ &options_.generate_java_class_path, Command::kPath);
AddOptionalFlag("--proguard", "Output file for generated Proguard rules.",
- &options_.generate_proguard_rules_path);
+ &options_.generate_proguard_rules_path, Command::kPath);
AddOptionalFlag("--proguard-main-dex",
"Output file for generated Proguard rules for the main dex.",
- &options_.generate_main_dex_proguard_rules_path);
+ &options_.generate_main_dex_proguard_rules_path, Command::kPath);
AddOptionalSwitch("--proguard-conditional-keep-rules",
"Generate conditional Proguard keep rules.",
&options_.generate_conditional_proguard_rules);
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
index 43bc216..d07305b 100644
--- a/tools/aapt2/cmd/Optimize.h
+++ b/tools/aapt2/cmd/Optimize.h
@@ -61,9 +61,10 @@
public:
explicit OptimizeCommand() : Command("optimize") {
SetDescription("Preforms resource optimizations on an apk.");
- AddOptionalFlag("-o", "Path to the output APK.", &options_.output_path);
- AddOptionalFlag("-d", "Path to the output directory (for splits).", &options_.output_dir);
- AddOptionalFlag("-x", "Path to XML configuration file.", &config_path_);
+ AddOptionalFlag("-o", "Path to the output APK.", &options_.output_path, Command::kPath);
+ AddOptionalFlag("-d", "Path to the output directory (for splits).", &options_.output_dir,
+ Command::kPath);
+ AddOptionalFlag("-x", "Path to XML configuration file.", &config_path_, Command::kPath);
AddOptionalSwitch("-p", "Print the multi APK artifacts and exit.", &print_only_);
AddOptionalFlag(
"--target-densities",
diff --git a/wifi/java/android/net/wifi/DppStatusCallback.java b/wifi/java/android/net/wifi/DppStatusCallback.java
new file mode 100644
index 0000000..fa2ab30
--- /dev/null
+++ b/wifi/java/android/net/wifi/DppStatusCallback.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.Handler;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * DPP Status Callback. Use this callback to get status updates (success, failure, progress)
+ * from the DPP operation started with {@link WifiManager#startDppAsConfiguratorInitiator(String,
+ * int, int, Handler, DppStatusCallback)} or {@link WifiManager#startDppAsEnrolleeInitiator(String,
+ * Handler, DppStatusCallback)}
+ * @hide
+ */
+@SystemApi
+public abstract class DppStatusCallback {
+ /**
+ * DPP Success event: Configuration sent (Configurator mode).
+ */
+ public static final int DPP_EVENT_SUCCESS_CONFIGURATION_SENT = 0;
+
+ /** @hide */
+ @IntDef(prefix = { "DPP_EVENT_SUCCESS_" }, value = {
+ DPP_EVENT_SUCCESS_CONFIGURATION_SENT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DppSuccessStatusCode {}
+
+ /**
+ * DPP Progress event: Initial authentication with peer succeeded.
+ */
+ public static final int DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0;
+
+ /**
+ * DPP Progress event: Peer requires more time to process bootstrapping.
+ */
+ public static final int DPP_EVENT_PROGRESS_RESPONSE_PENDING = 1;
+
+ /** @hide */
+ @IntDef(prefix = { "DPP_EVENT_PROGRESS_" }, value = {
+ DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS,
+ DPP_EVENT_PROGRESS_RESPONSE_PENDING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DppProgressStatusCode {}
+
+ /**
+ * DPP Failure event: Scanned QR code is either not a DPP URI, or the DPP URI has errors.
+ */
+ public static final int DPP_EVENT_FAILURE_INVALID_URI = -1;
+
+ /**
+ * DPP Failure event: Bootstrapping/Authentication initialization process failure.
+ */
+ public static final int DPP_EVENT_FAILURE_AUTHENTICATION = -2;
+
+ /**
+ * DPP Failure event: Both devices are implementing the same role and are incompatible.
+ */
+ public static final int DPP_EVENT_FAILURE_NOT_COMPATIBLE = -3;
+
+ /**
+ * DPP Failure event: Configuration process has failed due to malformed message.
+ */
+ public static final int DPP_EVENT_FAILURE_CONFIGURATION = -4;
+
+ /**
+ * DPP Failure event: DPP request while in another DPP exchange.
+ */
+ public static final int DPP_EVENT_FAILURE_BUSY = -5;
+
+ /**
+ * DPP Failure event: No response from the peer.
+ */
+ public static final int DPP_EVENT_FAILURE_TIMEOUT = -6;
+
+ /**
+ * DPP Failure event: General protocol failure.
+ */
+ public static final int DPP_EVENT_FAILURE = -7;
+
+ /**
+ * DPP Failure event: Feature or option is not supported.
+ */
+ public static final int DPP_EVENT_FAILURE_NOT_SUPPORTED = -8;
+
+ /**
+ * DPP Failure event: Invalid network provided to DPP configurator.
+ * Network must either be WPA3-Personal (SAE) or WPA2-Personal (PSK).
+ */
+ public static final int DPP_EVENT_FAILURE_INVALID_NETWORK = -9;
+
+
+ /** @hide */
+ @IntDef(prefix = {"DPP_EVENT_FAILURE_"}, value = {
+ DPP_EVENT_FAILURE_INVALID_URI,
+ DPP_EVENT_FAILURE_AUTHENTICATION,
+ DPP_EVENT_FAILURE_NOT_COMPATIBLE,
+ DPP_EVENT_FAILURE_CONFIGURATION,
+ DPP_EVENT_FAILURE_BUSY,
+ DPP_EVENT_FAILURE_TIMEOUT,
+ DPP_EVENT_FAILURE,
+ DPP_EVENT_FAILURE_NOT_SUPPORTED,
+ DPP_EVENT_FAILURE_INVALID_NETWORK,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DppFailureStatusCode {
+ }
+
+ /**
+ * Called when local DPP Enrollee successfully receives a new Wi-Fi configuration from the
+ * peer DPP configurator. This callback marks the successful end of the DPP current DPP
+ * session, and no further callbacks will be called. This callback is the successful outcome
+ * of a DPP flow starting with {@link WifiManager#startDppAsEnrolleeInitiator(String, Handler,
+ * DppStatusCallback)}.
+ *
+ * @param newNetworkId New Wi-Fi configuration with a network ID received from the configurator
+ */
+ public abstract void onEnrolleeSuccess(int newNetworkId);
+
+ /**
+ * Called when a DPP success event takes place, except for when configuration is received from
+ * an external Configurator. The callback onSuccessConfigReceived will be used in this case.
+ * This callback marks the successful end of the current DPP session, and no further
+ * callbacks will be called. This callback is the successful outcome of a DPP flow starting with
+ * {@link WifiManager#startDppAsConfiguratorInitiator(String, int, int, Handler,
+ * DppStatusCallback)}.
+ *
+ * @param code DPP success status code.
+ */
+ public abstract void onConfiguratorSuccess(@DppSuccessStatusCode int code);
+
+ /**
+ * Called when a DPP Failure event takes place. This callback marks the unsuccessful end of the
+ * current DPP session, and no further callbacks will be called.
+ *
+ * @param code DPP failure status code.
+ */
+ public abstract void onFailure(@DppFailureStatusCode int code);
+
+ /**
+ * Called when DPP events that indicate progress take place. Can be used by UI elements
+ * to show progress.
+ *
+ * @param code DPP progress status code.
+ */
+ public abstract void onProgress(@DppProgressStatusCode int code);
+}
diff --git a/wifi/java/android/net/wifi/IDppCallback.aidl b/wifi/java/android/net/wifi/IDppCallback.aidl
new file mode 100644
index 0000000..c452c76
--- /dev/null
+++ b/wifi/java/android/net/wifi/IDppCallback.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+/**
+ * Interface for DPP callback.
+ *
+ * @hide
+ */
+oneway interface IDppCallback
+{
+ /**
+ * Called when local DPP Enrollee successfully receives a new Wi-Fi configuratrion from the
+ * peer DPP configurator.
+ */
+ void onSuccessConfigReceived(int newNetworkId);
+
+ /**
+ * Called when DPP success events take place, except for when configuration is received from
+ * an external Configurator. The callback onSuccessConfigReceived will be used in this case.
+ */
+ void onSuccess(int status);
+
+ /**
+ * Called when DPP Failure events take place.
+ */
+ void onFailure(int status);
+
+ /**
+ * Called when DPP events that indicate progress take place. Can be used by UI elements
+ * to show progress.
+ */
+ void onProgress(int status);
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 0362a1b..1700006 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -25,6 +25,7 @@
import android.net.DhcpInfo;
import android.net.Network;
+import android.net.wifi.IDppCallback;
import android.net.wifi.INetworkRequestMatchCallback;
import android.net.wifi.ISoftApCallback;
import android.net.wifi.ITrafficStateCallback;
@@ -199,5 +200,13 @@
String[] getFactoryMacAddresses();
void setDeviceMobilityState(int state);
+
+ void startDppAsConfiguratorInitiator(in IBinder binder, in String enrolleeUri,
+ int selectedNetworkId, int netRole, in IDppCallback callback);
+
+ void startDppAsEnrolleeInitiator(in IBinder binder, in String configuratorUri,
+ in IDppCallback callback);
+
+ void stopDppSession();
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index a7c2ff0..e67e8ea 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2017,6 +2017,8 @@
public static final int WIFI_FEATURE_OWE = 0x20000000; // Enhanced Open
/** @hide */
public static final int WIFI_FEATURE_LOW_LATENCY = 0x40000000; // Low Latency modes
+ /** @hide */
+ public static final int WIFI_FEATURE_DPP = 0x80000000; // DPP (Easy-Connect)
private int getSupportedFeatures() {
try {
@@ -4463,6 +4465,13 @@
}
/**
+ * @return true if this device supports Wi-Fi Device Provisioning Protocol (Easy-connect)
+ */
+ public boolean isDppSupported() {
+ return isFeatureSupported(WIFI_FEATURE_DPP);
+ }
+
+ /**
* Gets the factory Wi-Fi MAC addresses.
* @return Array of String representing Wi-Fi MAC addresses sorted lexically or an empty Array
* if failed.
@@ -4541,4 +4550,146 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /* DPP - Device Provisioning Protocol AKA "Easy Connect" */
+
+ /**
+ * DPP Network role: Station.
+ * @hide
+ */
+ @SystemApi
+ public static final int DPP_NETWORK_ROLE_STA = 0;
+
+ /**
+ * DPP Network role: Access Point.
+ * @hide
+ */
+ @SystemApi
+ public static final int DPP_NETWORK_ROLE_AP = 1;
+
+ /** @hide */
+ @IntDef(prefix = {"DPP_NETWORK_ROLE_"}, value = {
+ DPP_NETWORK_ROLE_STA,
+ DPP_NETWORK_ROLE_AP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DppNetworkRole {}
+
+ /**
+ * Start DPP in Configurator-Initiator role. The current device will initiate DPP bootstrapping
+ * with a peer, and configure the peer with the SSID and password of the specified network using
+ * the DPP protocol on an encrypted link.
+ *
+ * @param enrolleeUri URI of the Enrollee obtained separately (e.g. QR code scanning)
+ * @param selectedNetworkId Selected network ID to be sent to the peer
+ * @param enrolleeNetworkRole The network role of the enrollee
+ * @param callback Callback for status updates
+ * @param handler The handler on whose thread to execute the callbacks. Null for main thread.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
+ public void startDppAsConfiguratorInitiator(@NonNull String enrolleeUri,
+ int selectedNetworkId, @DppNetworkRole int enrolleeNetworkRole,
+ @Nullable Handler handler, @NonNull DppStatusCallback callback) {
+ Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
+ Binder binder = new Binder();
+ try {
+ mService.startDppAsConfiguratorInitiator(binder, enrolleeUri, selectedNetworkId,
+ enrolleeNetworkRole, new DppCallbackProxy(looper, callback));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Start DPP in Enrollee-Initiator role. The current device will initiate DPP bootstrapping
+ * with a peer, and receive the SSID and password from the peer configurator.
+ *
+ * @param configuratorUri URI of the Configurator obtained separately (e.g. QR code scanning)
+ * @param callback Callback for status updates
+ * @param handler The handler on whose thread to execute the callbacks. Null for main thread.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
+ public void startDppAsEnrolleeInitiator(@NonNull String configuratorUri,
+ @Nullable Handler handler, @NonNull DppStatusCallback callback) {
+ Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
+ Binder binder = new Binder();
+ try {
+ mService.startDppAsEnrolleeInitiator(binder, configuratorUri,
+ new DppCallbackProxy(looper, callback));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Stop or abort a current DPP session.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
+ public void stopDppSession() {
+ try {
+ /* Request lower layers to stop/abort and clear resources */
+ mService.stopDppSession();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Helper class to support DPP callbacks
+ * @hide
+ */
+ @SystemApi
+ private static class DppCallbackProxy extends IDppCallback.Stub {
+ private final Handler mHandler;
+ private final DppStatusCallback mDppStatusCallback;
+
+ DppCallbackProxy(Looper looper, DppStatusCallback dppStatusCallback) {
+ mHandler = new Handler(looper);
+ mDppStatusCallback = dppStatusCallback;
+ }
+
+ @Override
+ public void onSuccessConfigReceived(int newNetworkId) {
+ Log.d(TAG, "DPP onSuccessConfigReceived callback");
+ mHandler.post(() -> {
+ mDppStatusCallback.onEnrolleeSuccess(newNetworkId);
+ });
+ }
+
+ @Override
+ public void onSuccess(int status) {
+ Log.d(TAG, "DPP onSuccess callback");
+ mHandler.post(() -> {
+ mDppStatusCallback.onConfiguratorSuccess(status);
+ });
+ }
+
+ @Override
+ public void onFailure(int status) {
+ Log.d(TAG, "DPP onFailure callback");
+ mHandler.post(() -> {
+ mDppStatusCallback.onFailure(status);
+ });
+ }
+
+ @Override
+ public void onProgress(int status) {
+ Log.d(TAG, "DPP onProgress callback");
+ mHandler.post(() -> {
+ mDppStatusCallback.onProgress(status);
+ });
+ }
+ }
}