Merge "Add Email.ENTEPRISE_CONTENT_FILTER_URI"
diff --git a/api/current.txt b/api/current.txt
index 8136502..24b4570 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5802,6 +5802,7 @@
method public void lockNow();
method public void removeActiveAdmin(android.content.ComponentName);
method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
+ method public boolean removeKeyPair(android.content.ComponentName, java.lang.String);
method public boolean removeUser(android.content.ComponentName, android.os.UserHandle);
method public boolean resetPassword(java.lang.String, int);
method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
@@ -7779,6 +7780,8 @@
method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public abstract deprecated void clearWallpaper() throws java.io.IOException;
method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
+ method public abstract android.content.Context createCredentialEncryptedContext(android.content.Context);
+ method public abstract android.content.Context createDeviceEncryptedContext(android.content.Context);
method public abstract android.content.Context createDisplayContext(android.view.Display);
method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract java.lang.String[] databaseList();
@@ -7802,7 +7805,6 @@
method public final android.content.res.ColorStateList getColorStateList(int);
method public abstract android.content.ContentResolver getContentResolver();
method public abstract java.io.File getDatabasePath(java.lang.String);
- method public abstract java.io.File getDeviceEncryptedFilesDir();
method public abstract java.io.File getDir(java.lang.String, int);
method public final android.graphics.drawable.Drawable getDrawable(int);
method public abstract java.io.File getExternalCacheDir();
@@ -7835,6 +7837,8 @@
method public abstract deprecated int getWallpaperDesiredMinimumHeight();
method public abstract deprecated int getWallpaperDesiredMinimumWidth();
method public abstract void grantUriPermission(java.lang.String, android.net.Uri, int);
+ method public abstract boolean isCredentialEncrypted();
+ method public abstract boolean isDeviceEncrypted();
method public boolean isRestricted();
method public final android.content.res.TypedArray obtainStyledAttributes(int[]);
method public final android.content.res.TypedArray obtainStyledAttributes(int, int[]) throws android.content.res.Resources.NotFoundException;
@@ -7966,6 +7970,8 @@
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public deprecated void clearWallpaper() throws java.io.IOException;
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+ method public android.content.Context createCredentialEncryptedContext(android.content.Context);
+ method public android.content.Context createDeviceEncryptedContext(android.content.Context);
method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
@@ -7988,7 +7994,6 @@
method public java.io.File getCodeCacheDir();
method public android.content.ContentResolver getContentResolver();
method public java.io.File getDatabasePath(java.lang.String);
- method public java.io.File getDeviceEncryptedFilesDir();
method public java.io.File getDir(java.lang.String, int);
method public java.io.File getExternalCacheDir();
method public java.io.File[] getExternalCacheDirs();
@@ -8016,6 +8021,8 @@
method public deprecated int getWallpaperDesiredMinimumHeight();
method public deprecated int getWallpaperDesiredMinimumWidth();
method public void grantUriPermission(java.lang.String, android.net.Uri, int);
+ method public boolean isCredentialEncrypted();
+ method public boolean isDeviceEncrypted();
method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -9097,8 +9104,10 @@
field public java.lang.String backupAgentName;
field public java.lang.String className;
field public int compatibleWidthLimitDp;
+ field public java.lang.String credentialEncryptedDataDir;
field public java.lang.String dataDir;
field public int descriptionRes;
+ field public java.lang.String deviceEncryptedDataDir;
field public boolean enabled;
field public int flags;
field public int largestWidthLimitDp;
@@ -9420,6 +9429,8 @@
method public abstract int getComponentEnabledSetting(android.content.ComponentName);
method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
+ method public abstract byte[] getEphemeralCookie();
+ method public abstract int getEphemeralCookieMaxSizeBytes();
method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public abstract java.lang.String getInstallerPackageName(java.lang.String);
@@ -9451,6 +9462,7 @@
method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
method public abstract boolean hasSystemFeature(java.lang.String);
+ method public abstract boolean isEphemeralApplication();
method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
method public abstract boolean isSafeMode();
method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9468,6 +9480,7 @@
method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
+ method public abstract boolean setEphemeralCookie(byte[]);
method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
method public abstract void verifyPendingInstall(int, int);
field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
@@ -36235,6 +36248,8 @@
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper();
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+ method public android.content.Context createCredentialEncryptedContext(android.content.Context);
+ method public android.content.Context createDeviceEncryptedContext(android.content.Context);
method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
@@ -36256,7 +36271,6 @@
method public java.io.File getCodeCacheDir();
method public android.content.ContentResolver getContentResolver();
method public java.io.File getDatabasePath(java.lang.String);
- method public java.io.File getDeviceEncryptedFilesDir();
method public java.io.File getDir(java.lang.String, int);
method public java.io.File getExternalCacheDir();
method public java.io.File[] getExternalCacheDirs();
@@ -36284,6 +36298,8 @@
method public int getWallpaperDesiredMinimumHeight();
method public int getWallpaperDesiredMinimumWidth();
method public void grantUriPermission(java.lang.String, android.net.Uri, int);
+ method public boolean isCredentialEncrypted();
+ method public boolean isDeviceEncrypted();
method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -36407,6 +36423,8 @@
method public android.graphics.drawable.Drawable getDefaultActivityIcon();
method public java.lang.String getDefaultBrowserPackageName(int);
method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
+ method public byte[] getEphemeralCookie();
+ method public int getEphemeralCookieMaxSizeBytes();
method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public java.lang.String getInstallerPackageName(java.lang.String);
@@ -36437,6 +36455,7 @@
method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
method public boolean hasSystemFeature(java.lang.String);
+ method public boolean isEphemeralApplication();
method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
method public boolean isSafeMode();
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -36455,6 +36474,7 @@
method public void setApplicationEnabledSetting(java.lang.String, int, int);
method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
method public boolean setDefaultBrowserPackageName(java.lang.String, int);
+ method public boolean setEphemeralCookie(byte[]);
method public void setInstallerPackageName(java.lang.String, java.lang.String);
method public void verifyPendingInstall(int, int);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 77a1fa6..3c10294 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5935,6 +5935,7 @@
method public void notifyPendingSystemUpdate(long);
method public void removeActiveAdmin(android.content.ComponentName);
method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
+ method public boolean removeKeyPair(android.content.ComponentName, java.lang.String);
method public boolean removeUser(android.content.ComponentName, android.os.UserHandle);
method public boolean resetPassword(java.lang.String, int);
method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
@@ -8023,6 +8024,8 @@
method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public abstract deprecated void clearWallpaper() throws java.io.IOException;
method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
+ method public abstract android.content.Context createCredentialEncryptedContext(android.content.Context);
+ method public abstract android.content.Context createDeviceEncryptedContext(android.content.Context);
method public abstract android.content.Context createDisplayContext(android.view.Display);
method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract java.lang.String[] databaseList();
@@ -8045,9 +8048,7 @@
method public final int getColor(int);
method public final android.content.res.ColorStateList getColorStateList(int);
method public abstract android.content.ContentResolver getContentResolver();
- method public abstract java.io.File getCredentialEncryptedFilesDir();
method public abstract java.io.File getDatabasePath(java.lang.String);
- method public abstract java.io.File getDeviceEncryptedFilesDir();
method public abstract java.io.File getDir(java.lang.String, int);
method public final android.graphics.drawable.Drawable getDrawable(int);
method public abstract java.io.File getExternalCacheDir();
@@ -8080,6 +8081,8 @@
method public abstract deprecated int getWallpaperDesiredMinimumHeight();
method public abstract deprecated int getWallpaperDesiredMinimumWidth();
method public abstract void grantUriPermission(java.lang.String, android.net.Uri, int);
+ method public abstract boolean isCredentialEncrypted();
+ method public abstract boolean isDeviceEncrypted();
method public boolean isRestricted();
method public final android.content.res.TypedArray obtainStyledAttributes(int[]);
method public final android.content.res.TypedArray obtainStyledAttributes(int, int[]) throws android.content.res.Resources.NotFoundException;
@@ -8219,6 +8222,8 @@
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public deprecated void clearWallpaper() throws java.io.IOException;
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+ method public android.content.Context createCredentialEncryptedContext(android.content.Context);
+ method public android.content.Context createDeviceEncryptedContext(android.content.Context);
method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
@@ -8240,9 +8245,7 @@
method public java.lang.ClassLoader getClassLoader();
method public java.io.File getCodeCacheDir();
method public android.content.ContentResolver getContentResolver();
- method public java.io.File getCredentialEncryptedFilesDir();
method public java.io.File getDatabasePath(java.lang.String);
- method public java.io.File getDeviceEncryptedFilesDir();
method public java.io.File getDir(java.lang.String, int);
method public java.io.File getExternalCacheDir();
method public java.io.File[] getExternalCacheDirs();
@@ -8270,6 +8273,8 @@
method public deprecated int getWallpaperDesiredMinimumHeight();
method public deprecated int getWallpaperDesiredMinimumWidth();
method public void grantUriPermission(java.lang.String, android.net.Uri, int);
+ method public boolean isCredentialEncrypted();
+ method public boolean isDeviceEncrypted();
method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -9361,8 +9366,10 @@
field public java.lang.String backupAgentName;
field public java.lang.String className;
field public int compatibleWidthLimitDp;
+ field public java.lang.String credentialEncryptedDataDir;
field public java.lang.String dataDir;
field public int descriptionRes;
+ field public java.lang.String deviceEncryptedDataDir;
field public boolean enabled;
field public int flags;
field public int largestWidthLimitDp;
@@ -9712,6 +9719,8 @@
method public abstract int getComponentEnabledSetting(android.content.ComponentName);
method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
+ method public abstract byte[] getEphemeralCookie();
+ method public abstract int getEphemeralCookieMaxSizeBytes();
method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public abstract java.lang.String getInstallerPackageName(java.lang.String);
@@ -9745,6 +9754,7 @@
method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract boolean hasSystemFeature(java.lang.String);
+ method public abstract boolean isEphemeralApplication();
method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
method public abstract boolean isSafeMode();
method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9764,6 +9774,7 @@
method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
+ method public abstract boolean setEphemeralCookie(byte[]);
method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
method public abstract void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
@@ -38561,6 +38572,8 @@
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper();
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+ method public android.content.Context createCredentialEncryptedContext(android.content.Context);
+ method public android.content.Context createDeviceEncryptedContext(android.content.Context);
method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
@@ -38581,9 +38594,7 @@
method public java.lang.ClassLoader getClassLoader();
method public java.io.File getCodeCacheDir();
method public android.content.ContentResolver getContentResolver();
- method public java.io.File getCredentialEncryptedFilesDir();
method public java.io.File getDatabasePath(java.lang.String);
- method public java.io.File getDeviceEncryptedFilesDir();
method public java.io.File getDir(java.lang.String, int);
method public java.io.File getExternalCacheDir();
method public java.io.File[] getExternalCacheDirs();
@@ -38611,6 +38622,8 @@
method public int getWallpaperDesiredMinimumHeight();
method public int getWallpaperDesiredMinimumWidth();
method public void grantUriPermission(java.lang.String, android.net.Uri, int);
+ method public boolean isCredentialEncrypted();
+ method public boolean isDeviceEncrypted();
method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -38737,6 +38750,8 @@
method public android.graphics.drawable.Drawable getDefaultActivityIcon();
method public java.lang.String getDefaultBrowserPackageName(int);
method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
+ method public byte[] getEphemeralCookie();
+ method public int getEphemeralCookieMaxSizeBytes();
method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public java.lang.String getInstallerPackageName(java.lang.String);
@@ -38769,6 +38784,7 @@
method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
method public void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public boolean hasSystemFeature(java.lang.String);
+ method public boolean isEphemeralApplication();
method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
method public boolean isSafeMode();
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -38789,6 +38805,7 @@
method public void setApplicationEnabledSetting(java.lang.String, int, int);
method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
method public boolean setDefaultBrowserPackageName(java.lang.String, int);
+ method public boolean setEphemeralCookie(byte[]);
method public void setInstallerPackageName(java.lang.String, java.lang.String);
method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
method public void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
diff --git a/api/test-current.txt b/api/test-current.txt
index ca8591f..c46a4ea 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5802,6 +5802,7 @@
method public void lockNow();
method public void removeActiveAdmin(android.content.ComponentName);
method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
+ method public boolean removeKeyPair(android.content.ComponentName, java.lang.String);
method public boolean removeUser(android.content.ComponentName, android.os.UserHandle);
method public boolean resetPassword(java.lang.String, int);
method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
@@ -7779,6 +7780,8 @@
method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public abstract deprecated void clearWallpaper() throws java.io.IOException;
method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
+ method public abstract android.content.Context createCredentialEncryptedContext(android.content.Context);
+ method public abstract android.content.Context createDeviceEncryptedContext(android.content.Context);
method public abstract android.content.Context createDisplayContext(android.view.Display);
method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract java.lang.String[] databaseList();
@@ -7802,7 +7805,6 @@
method public final android.content.res.ColorStateList getColorStateList(int);
method public abstract android.content.ContentResolver getContentResolver();
method public abstract java.io.File getDatabasePath(java.lang.String);
- method public abstract java.io.File getDeviceEncryptedFilesDir();
method public abstract java.io.File getDir(java.lang.String, int);
method public final android.graphics.drawable.Drawable getDrawable(int);
method public abstract java.io.File getExternalCacheDir();
@@ -7835,6 +7837,8 @@
method public abstract deprecated int getWallpaperDesiredMinimumHeight();
method public abstract deprecated int getWallpaperDesiredMinimumWidth();
method public abstract void grantUriPermission(java.lang.String, android.net.Uri, int);
+ method public abstract boolean isCredentialEncrypted();
+ method public abstract boolean isDeviceEncrypted();
method public boolean isRestricted();
method public final android.content.res.TypedArray obtainStyledAttributes(int[]);
method public final android.content.res.TypedArray obtainStyledAttributes(int, int[]) throws android.content.res.Resources.NotFoundException;
@@ -7966,6 +7970,8 @@
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public deprecated void clearWallpaper() throws java.io.IOException;
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+ method public android.content.Context createCredentialEncryptedContext(android.content.Context);
+ method public android.content.Context createDeviceEncryptedContext(android.content.Context);
method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
@@ -7988,7 +7994,6 @@
method public java.io.File getCodeCacheDir();
method public android.content.ContentResolver getContentResolver();
method public java.io.File getDatabasePath(java.lang.String);
- method public java.io.File getDeviceEncryptedFilesDir();
method public java.io.File getDir(java.lang.String, int);
method public java.io.File getExternalCacheDir();
method public java.io.File[] getExternalCacheDirs();
@@ -8016,6 +8021,8 @@
method public deprecated int getWallpaperDesiredMinimumHeight();
method public deprecated int getWallpaperDesiredMinimumWidth();
method public void grantUriPermission(java.lang.String, android.net.Uri, int);
+ method public boolean isCredentialEncrypted();
+ method public boolean isDeviceEncrypted();
method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -9097,8 +9104,10 @@
field public java.lang.String backupAgentName;
field public java.lang.String className;
field public int compatibleWidthLimitDp;
+ field public java.lang.String credentialEncryptedDataDir;
field public java.lang.String dataDir;
field public int descriptionRes;
+ field public java.lang.String deviceEncryptedDataDir;
field public boolean enabled;
field public int flags;
field public int largestWidthLimitDp;
@@ -9420,6 +9429,8 @@
method public abstract int getComponentEnabledSetting(android.content.ComponentName);
method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
+ method public abstract byte[] getEphemeralCookie();
+ method public abstract int getEphemeralCookieMaxSizeBytes();
method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public abstract java.lang.String getInstallerPackageName(java.lang.String);
@@ -9451,6 +9462,7 @@
method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
method public abstract boolean hasSystemFeature(java.lang.String);
+ method public abstract boolean isEphemeralApplication();
method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
method public abstract boolean isSafeMode();
method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9468,6 +9480,7 @@
method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
+ method public abstract boolean setEphemeralCookie(byte[]);
method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
method public abstract void verifyPendingInstall(int, int);
field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
@@ -36237,6 +36250,8 @@
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper();
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+ method public android.content.Context createCredentialEncryptedContext(android.content.Context);
+ method public android.content.Context createDeviceEncryptedContext(android.content.Context);
method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
@@ -36258,7 +36273,6 @@
method public java.io.File getCodeCacheDir();
method public android.content.ContentResolver getContentResolver();
method public java.io.File getDatabasePath(java.lang.String);
- method public java.io.File getDeviceEncryptedFilesDir();
method public java.io.File getDir(java.lang.String, int);
method public java.io.File getExternalCacheDir();
method public java.io.File[] getExternalCacheDirs();
@@ -36286,6 +36300,8 @@
method public int getWallpaperDesiredMinimumHeight();
method public int getWallpaperDesiredMinimumWidth();
method public void grantUriPermission(java.lang.String, android.net.Uri, int);
+ method public boolean isCredentialEncrypted();
+ method public boolean isDeviceEncrypted();
method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -36409,6 +36425,8 @@
method public android.graphics.drawable.Drawable getDefaultActivityIcon();
method public java.lang.String getDefaultBrowserPackageName(int);
method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
+ method public byte[] getEphemeralCookie();
+ method public int getEphemeralCookieMaxSizeBytes();
method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public java.lang.String getInstallerPackageName(java.lang.String);
@@ -36439,6 +36457,7 @@
method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
method public boolean hasSystemFeature(java.lang.String);
+ method public boolean isEphemeralApplication();
method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
method public boolean isSafeMode();
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -36457,6 +36476,7 @@
method public void setApplicationEnabledSetting(java.lang.String, int, int);
method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
method public boolean setDefaultBrowserPackageName(java.lang.String, int);
+ method public boolean setEphemeralCookie(byte[]);
method public void setInstallerPackageName(java.lang.String, java.lang.String);
method public void verifyPendingInstall(int, int);
}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 544fd6a..ad1b3b5 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -16,11 +16,11 @@
package com.android.commands.pm;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import android.accounts.IAccountManager;
import android.app.ActivityManager;
@@ -139,19 +139,19 @@
}
if ("install-create".equals(op)) {
- return runInstallSession();
+ return runInstallCreate();
}
if ("install-write".equals(op)) {
- return runInstallSession();
+ return runInstallWrite();
}
if ("install-commit".equals(op)) {
- return runInstallSession();
+ return runInstallCommit();
}
if ("install-abandon".equals(op) || "install-destroy".equals(op)) {
- return runInstallSession();
+ return runInstallAbandon();
}
if ("set-installer".equals(op)) {
@@ -346,21 +346,203 @@
throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
}
- private void writeSessionFile(int sessionId, String path, long sizeBytes) throws RemoteException {
+ /*
+ * Keep this around to support existing users of the "pm install" command that may not be
+ * able to be updated [or, at least informed the API has changed] such as ddmlib.
+ *
+ * Moving the implementation of "pm install" to "cmd package install" changes the executing
+ * context. Instead of being a stand alone process, "cmd package install" runs in the
+ * system_server process. Due to SELinux rules, system_server cannot access many directories;
+ * one of which being the package install staging directory [/data/local/tmp].
+ *
+ * The use of "adb install" or "cmd package install" over "pm install" is highly encouraged.
+ */
+ private int runInstall() throws RemoteException {
+ final InstallParams params = makeInstallParams();
+ final int sessionId = doCreateSession(params.sessionParams,
+ params.installerPackageName, params.userId);
+
+ try {
+ final String inPath = nextArg();
+ if (inPath == null && params.sessionParams.sizeBytes == 0) {
+ System.err.println("Error: must either specify a package size or an APK file");
+ return 1;
+ }
+ if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
+ false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
+ return 1;
+ }
+ if (doCommitSession(sessionId, false /*logSuccess*/)
+ != PackageInstaller.STATUS_SUCCESS) {
+ return 1;
+ }
+ return 0;
+ } finally {
+ try {
+ mInstaller.abandonSession(sessionId);
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+ private int runInstallAbandon() throws RemoteException {
+ final int sessionId = Integer.parseInt(nextArg());
+ return doAbandonSession(sessionId, true /*logSuccess*/);
+ }
+
+ private int runInstallCommit() throws RemoteException {
+ final int sessionId = Integer.parseInt(nextArg());
+ return doCommitSession(sessionId, true /*logSuccess*/);
+ }
+
+ private int runInstallCreate() throws RemoteException {
+ final InstallParams installParams = makeInstallParams();
+ final int sessionId = doCreateSession(installParams.sessionParams,
+ installParams.installerPackageName, installParams.userId);
+
+ // NOTE: adb depends on parsing this string
+ System.out.println("Success: created install session [" + sessionId + "]");
+ return PackageInstaller.STATUS_SUCCESS;
+ }
+
+ private int runInstallWrite() throws RemoteException {
+ long sizeBytes = -1;
+
+ String opt;
+ while ((opt = nextOption()) != null) {
+ if (opt.equals("-S")) {
+ sizeBytes = Long.parseLong(nextArg());
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + opt);
+ }
+ }
+
+ final int sessionId = Integer.parseInt(nextArg());
+ final String splitName = nextArg();
+ final String path = nextArg();
+ return doWriteSession(sessionId, path, sizeBytes, splitName, true /*logSuccess*/);
+ }
+
+ private static class InstallParams {
+ SessionParams sessionParams;
+ String installerPackageName;
+ int userId = UserHandle.USER_ALL;
+ }
+
+ private InstallParams makeInstallParams() {
+ final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+ final InstallParams params = new InstallParams();
+ params.sessionParams = sessionParams;
+ String opt;
+ while ((opt = nextOption()) != null) {
+ switch (opt) {
+ case "-l":
+ sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
+ break;
+ case "-r":
+ sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+ break;
+ case "-i":
+ params.installerPackageName = nextArg();
+ if (params.installerPackageName == null) {
+ throw new IllegalArgumentException("Missing installer package");
+ }
+ break;
+ case "-t":
+ sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
+ break;
+ case "-s":
+ sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL;
+ break;
+ case "-f":
+ sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
+ break;
+ case "-d":
+ sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
+ break;
+ case "-g":
+ sessionParams.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
+ break;
+ case "--originating-uri":
+ sessionParams.originatingUri = Uri.parse(nextOptionData());
+ break;
+ case "--referrer":
+ sessionParams.referrerUri = Uri.parse(nextOptionData());
+ break;
+ case "-p":
+ sessionParams.mode = SessionParams.MODE_INHERIT_EXISTING;
+ sessionParams.appPackageName = nextOptionData();
+ if (sessionParams.appPackageName == null) {
+ throw new IllegalArgumentException("Missing inherit package name");
+ }
+ break;
+ case "-S":
+ sessionParams.setSize(Long.parseLong(nextOptionData()));
+ break;
+ case "--abi":
+ sessionParams.abiOverride = checkAbiArgument(nextOptionData());
+ break;
+ case "--ephemeral":
+ sessionParams.installFlags |= PackageManager.INSTALL_EPHEMERAL;
+ break;
+ case "--user":
+ params.userId = UserHandle.parseUserArg(nextOptionData());
+ break;
+ case "--install-location":
+ sessionParams.installLocation = Integer.parseInt(nextOptionData());
+ break;
+ case "--force-uuid":
+ sessionParams.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
+ sessionParams.volumeUuid = nextOptionData();
+ if ("internal".equals(sessionParams.volumeUuid)) {
+ sessionParams.volumeUuid = null;
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown option " + opt);
+ }
+ }
+ return params;
+ }
+
+ private int doCreateSession(SessionParams params, String installerPackageName, int userId)
+ throws RemoteException {
+ userId = translateUserId(userId, "runInstallCreate");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ params.installFlags |= PackageManager.INSTALL_ALL_USERS;
+ }
+
+ final int sessionId = mInstaller.createSession(params, installerPackageName, userId);
+ return sessionId;
+ }
+
+ private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName,
+ boolean logSuccess) throws RemoteException {
+ if ("-".equals(inPath)) {
+ inPath = null;
+ } else if (inPath != null) {
+ final File file = new File(inPath);
+ if (file.isFile()) {
+ sizeBytes = file.length();
+ }
+ }
+
final SessionInfo info = mInstaller.getSessionInfo(sessionId);
PackageInstaller.Session session = null;
InputStream in = null;
OutputStream out = null;
try {
- session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+ session = new PackageInstaller.Session(
+ mInstaller.openSession(sessionId));
- if (path == null) {
- in = new SizedInputStream(System.in, sizeBytes);
+ if (inPath != null) {
+ in = new FileInputStream(inPath);
} else {
- in = new FileInputStream(path);
+ in = new SizedInputStream(System.in, sizeBytes);
}
- out = session.openWrite("base.apk", 0, sizeBytes);
+ out = session.openWrite(splitName, 0, sizeBytes);
int total = 0;
byte[] buffer = new byte[65536];
@@ -375,7 +557,14 @@
}
}
session.fsync(out);
- } catch (IOException ignore) {
+
+ if (logSuccess) {
+ System.out.println("Success: streamed " + total + " bytes");
+ }
+ return PackageInstaller.STATUS_SUCCESS;
+ } catch (IOException e) {
+ System.err.println("Error: failed to write; " + e.getMessage());
+ return PackageInstaller.STATUS_FAILURE;
} finally {
IoUtils.closeQuietly(out);
IoUtils.closeQuietly(in);
@@ -383,10 +572,11 @@
}
}
- private int commitSessionFile(int sessionId) throws RemoteException {
+ private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException {
PackageInstaller.Session session = null;
try {
- session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+ session = new PackageInstaller.Session(
+ mInstaller.openSession(sessionId));
final LocalIntentReceiver receiver = new LocalIntentReceiver();
session.commit(receiver.getIntentSender());
@@ -395,10 +585,13 @@
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
if (status == PackageInstaller.STATUS_SUCCESS) {
- System.out.println("Success");
+ if (logSuccess) {
+ System.out.println("Success");
+ }
} else {
System.err.println("Failure ["
+ result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
+ System.err.println("Failure details: " + result.getExtras());
}
return status;
} finally {
@@ -406,100 +599,18 @@
}
}
- /*
- * Keep this around to support existing users of the "pm install" command that may not be
- * able to be updated [or, at least informed the API has changed] such as ddmlib.
- *
- * Moving the implementation of "pm install" to "cmd package install" changes the executing
- * context. Instead of being a stand alone process, "cmd package install" runs in the
- * system_server process. Due to SELinux rules, system_server cannot access many directories;
- * one of which being the package install staging directory [/data/local/tmp].
- *
- * The use of "adb install" or "cmd package install" over "pm install" is highly encouraged.
- */
- private int runInstall() throws RemoteException {
- int userId = UserHandle.USER_ALL;
- String installerPackageName = null;
-
- final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
-
- String opt;
- while ((opt = nextOption()) != null) {
- if (opt.equals("-l")) {
- params.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
- } else if (opt.equals("-r")) {
- params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
- } else if (opt.equals("-i")) {
- installerPackageName = nextArg();
- if (installerPackageName == null) {
- throw new IllegalArgumentException("Missing installer package");
- }
- } else if (opt.equals("-t")) {
- params.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
- } else if (opt.equals("-s")) {
- params.installFlags |= PackageManager.INSTALL_EXTERNAL;
- } else if (opt.equals("-f")) {
- params.installFlags |= PackageManager.INSTALL_INTERNAL;
- } else if (opt.equals("-d")) {
- params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
- } else if (opt.equals("-g")) {
- params.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
- } else if (opt.equals("--originating-uri")) {
- params.originatingUri = Uri.parse(nextOptionData());
- } else if (opt.equals("--referrer")) {
- params.referrerUri = Uri.parse(nextOptionData());
- } else if (opt.equals("-p")) {
- params.mode = SessionParams.MODE_INHERIT_EXISTING;
- params.appPackageName = nextOptionData();
- if (params.appPackageName == null) {
- throw new IllegalArgumentException("Missing inherit package name");
- }
- } else if (opt.equals("-S")) {
- params.setSize(Long.parseLong(nextOptionData()));
- } else if (opt.equals("--abi")) {
- params.abiOverride = checkAbiArgument(nextOptionData());
- } else if (opt.equals("--ephemeral")) {
- params.installFlags |= PackageManager.INSTALL_EPHEMERAL;
- } else if (opt.equals("--user")) {
- userId = Integer.parseInt(nextOptionData());
- } else if (opt.equals("--install-location")) {
- params.installLocation = Integer.parseInt(nextOptionData());
- } else if (opt.equals("--force-uuid")) {
- params.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
- params.volumeUuid = nextOptionData();
- if ("internal".equals(params.volumeUuid)) {
- params.volumeUuid = null;
- }
- } else {
- throw new IllegalArgumentException("Unknown option " + opt);
+ private int doAbandonSession(int sessionId, boolean logSuccess) throws RemoteException {
+ PackageInstaller.Session session = null;
+ try {
+ session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+ session.abandon();
+ if (logSuccess) {
+ System.out.println("Success");
}
+ return PackageInstaller.STATUS_SUCCESS;
+ } finally {
+ IoUtils.closeQuietly(session);
}
-
- userId = translateUserId(userId, "runInstallCreate");
- if (userId == UserHandle.USER_ALL) {
- userId = UserHandle.USER_SYSTEM;
- params.installFlags |= PackageManager.INSTALL_ALL_USERS;
- }
-
- long sizeBytes = params.sizeBytes;
- String path = nextArg();
- if ("-".equals(path)) {
- path = null;
- } else if (path != null) {
- final File file = new File(path);
- if (file.isFile()) {
- sizeBytes = file.length();
- }
- }
-
- final int sessionId = mInstaller.createSession(params, installerPackageName, userId);
-
- writeSessionFile(sessionId, path, sizeBytes);
- return commitSessionFile(sessionId);
- }
-
- private int runInstallSession() {
- return runShellCommand("package", mArgs);
}
/**
@@ -798,6 +909,10 @@
flags |= UserInfo.FLAG_MANAGED_PROFILE;
} else if ("--restricted".equals(opt)) {
flags |= UserInfo.FLAG_RESTRICTED;
+ } else if ("--ephemeral".equals(opt)) {
+ flags |= UserInfo.FLAG_EPHEMERAL;
+ } else if ("--guest".equals(opt)) {
+ flags |= UserInfo.FLAG_GUEST;
} else {
System.err.println("Error: unknown option " + opt);
return showUsage();
@@ -1356,7 +1471,7 @@
System.err.println(" pm get-install-location");
System.err.println(" pm set-permission-enforced PERMISSION [true|false]");
System.err.println(" pm trim-caches DESIRED_FREE_SPACE [internal|UUID]");
- System.err.println(" pm create-user [--profileOf USER_ID] [--managed] [--restricted] USER_NAME");
+ System.err.println(" pm create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral] [--guest] USER_NAME");
System.err.println(" pm remove-user USER_ID");
System.err.println(" pm get-max-users");
System.err.println("");
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index e500e15..460e68c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -30,6 +30,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.ContainerEncryptionParams;
+import android.content.pm.EphemeralApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IOnPermissionsChangeListener;
import android.content.pm.IPackageDataObserver;
@@ -85,9 +86,11 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;
import com.android.internal.util.UserIcons;
+import libcore.util.EmptyArray;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -98,6 +101,8 @@
private static final String TAG = "ApplicationPackageManager";
private final static boolean DEBUG_ICONS = false;
+ private static final int DEFAULT_EPHEMERAL_COOKIE_MAX_SIZE_BYTES = 16384; // 16KB
+
// Default flags to use with PackageManager when no flags are given.
private final static int sDefaultFlags = PackageManager.GET_SHARED_LIBRARY_FILES;
@@ -626,6 +631,80 @@
}
}
+ /** @hide */
+ @SuppressWarnings("unchecked")
+ @Override
+ public List<EphemeralApplicationInfo> getEphemeralApplications() {
+ try {
+ ParceledListSlice<EphemeralApplicationInfo> slice =
+ mPM.getEphemeralApplications(mContext.getUserId());
+ if (slice != null) {
+ return slice.getList();
+ }
+ return Collections.emptyList();
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ /** @hide */
+ @Override
+ public Drawable getEphemeralApplicationIcon(String packageName) {
+ try {
+ Bitmap bitmap = mPM.getEphemeralApplicationIcon(
+ packageName, mContext.getUserId());
+ if (bitmap != null) {
+ return new BitmapDrawable(null, bitmap);
+ }
+ return null;
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public boolean isEphemeralApplication() {
+ try {
+ return mPM.isEphemeralApplication(
+ mContext.getPackageName(), mContext.getUserId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "System server is dead", e);
+ }
+ return false;
+ }
+
+ @Override
+ public int getEphemeralCookieMaxSizeBytes() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
+ DEFAULT_EPHEMERAL_COOKIE_MAX_SIZE_BYTES);
+ }
+
+ @Override
+ public @NonNull byte[] getEphemeralCookie() {
+ try {
+ final byte[] cookie = mPM.getEphemeralApplicationCookie(
+ mContext.getPackageName(), mContext.getUserId());
+ if (cookie != null) {
+ return cookie;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "System server is dead", e);
+ }
+ return EmptyArray.BYTE;
+ }
+
+ @Override
+ public boolean setEphemeralCookie(@NonNull byte[] cookie) {
+ try {
+ return mPM.setEphemeralApplicationCookie(
+ mContext.getPackageName(), cookie, mContext.getUserId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "System server is dead", e);
+ }
+ return false;
+ }
+
@Override
public ResolveInfo resolveActivity(Intent intent, int flags) {
return resolveActivityAsUser(intent, flags, mContext.getUserId());
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index a703c53..36e98f9 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -74,6 +74,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Objects;
class ReceiverRestrictedContext extends ContextWrapper {
ReceiverRestrictedContext(Context base) {
@@ -148,7 +149,7 @@
private final Display mDisplay; // may be null if default display
private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
- private final boolean mRestricted;
+ private final int mFlags;
private Context mOuterContext;
private int mThemeResource = 0;
@@ -443,22 +444,6 @@
}
@Override
- public File getDeviceEncryptedFilesDir() {
- if (mPackageInfo != null) {
- return mPackageInfo.getDeviceEncryptedDataDirFile();
- }
- throw new RuntimeException("Not supported in system context");
- }
-
- @Override
- public File getCredentialEncryptedFilesDir() {
- if (mPackageInfo != null) {
- return mPackageInfo.getCredentialEncryptedDataDirFile();
- }
- throw new RuntimeException("Not supported in system context");
- }
-
- @Override
public File getNoBackupFilesDir() {
synchronized (mSync) {
if (mNoBackupFilesDir == null) {
@@ -1684,9 +1669,8 @@
LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE);
if (pi != null) {
- final boolean restricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,
- new UserHandle(UserHandle.getUserId(application.uid)), restricted,
+ new UserHandle(UserHandle.getUserId(application.uid)), flags,
mDisplay, null, Display.INVALID_DISPLAY);
if (c.mResources != null) {
return c;
@@ -1707,17 +1691,16 @@
@Override
public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
throws NameNotFoundException {
- final boolean restricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
if (packageName.equals("system") || packageName.equals("android")) {
return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
- user, restricted, mDisplay, null, Display.INVALID_DISPLAY);
+ user, flags, mDisplay, null, Display.INVALID_DISPLAY);
}
LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
if (pi != null) {
ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,
- user, restricted, mDisplay, null, Display.INVALID_DISPLAY);
+ user, flags, mDisplay, null, Display.INVALID_DISPLAY);
if (c.mResources != null) {
return c;
}
@@ -1735,7 +1718,7 @@
}
return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
- mUser, mRestricted, mDisplay, overrideConfiguration, Display.INVALID_DISPLAY);
+ mUser, mFlags, mDisplay, overrideConfiguration, Display.INVALID_DISPLAY);
}
@Override
@@ -1745,7 +1728,7 @@
}
return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
- mUser, mRestricted, display, null, Display.INVALID_DISPLAY);
+ mUser, mFlags, display, null, Display.INVALID_DISPLAY);
}
Display getDisplay() {
@@ -1761,8 +1744,34 @@
}
@Override
+ public Context createDeviceEncryptedContext(Context context) {
+ final int flags = (mFlags & ~Context.CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED)
+ | Context.CONTEXT_STORAGE_DEVICE_ENCRYPTED;
+ return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
+ mUser, flags, mDisplay, null, Display.INVALID_DISPLAY);
+ }
+
+ @Override
+ public Context createCredentialEncryptedContext(Context context) {
+ final int flags = (mFlags & ~Context.CONTEXT_STORAGE_DEVICE_ENCRYPTED)
+ | Context.CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED;
+ return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
+ mUser, flags, mDisplay, null, Display.INVALID_DISPLAY);
+ }
+
+ @Override
public boolean isRestricted() {
- return mRestricted;
+ return (mFlags & Context.CONTEXT_RESTRICTED) != 0;
+ }
+
+ @Override
+ public boolean isDeviceEncrypted() {
+ return (mFlags & Context.CONTEXT_STORAGE_DEVICE_ENCRYPTED) != 0;
+ }
+
+ @Override
+ public boolean isCredentialEncrypted() {
+ return (mFlags & Context.CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED) != 0;
}
@Override
@@ -1772,7 +1781,14 @@
private File getDataDirFile() {
if (mPackageInfo != null) {
- return mPackageInfo.getDataDirFile();
+ if (isCredentialEncrypted()) {
+ return mPackageInfo.getCredentialEncryptedDataDirFile();
+ } else if (isDeviceEncrypted()) {
+ return mPackageInfo.getDeviceEncryptedDataDirFile();
+ } else {
+ throw new RuntimeException(
+ "Storage location is neither credential nor device encrypted");
+ }
}
throw new RuntimeException("Not supported in system context");
}
@@ -1798,7 +1814,7 @@
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
ContextImpl context = new ContextImpl(null, mainThread,
- packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY);
+ packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);
context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
context.mResourcesManager.getDisplayMetricsLocked());
return context;
@@ -1807,24 +1823,38 @@
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
return new ContextImpl(null, mainThread,
- packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY);
+ packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);
}
static ContextImpl createActivityContext(ActivityThread mainThread,
LoadedApk packageInfo, int displayId, Configuration overrideConfiguration) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
- return new ContextImpl(null, mainThread, packageInfo, null, null, false,
+ return new ContextImpl(null, mainThread, packageInfo, null, null, 0,
null, overrideConfiguration, displayId);
}
private ContextImpl(ContextImpl container, ActivityThread mainThread,
- LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
+ LoadedApk packageInfo, IBinder activityToken, UserHandle user, int flags,
Display display, Configuration overrideConfiguration, int createDisplayWithId) {
mOuterContext = this;
+ // If creator didn't specify which storage to use, use the default
+ // location for application.
+ if ((flags & Context.CONTEXT_STORAGE_MASK) == 0) {
+ final File dataDir = packageInfo.getDataDirFile();
+ if (Objects.equals(dataDir, packageInfo.getCredentialEncryptedDataDirFile())) {
+ flags |= Context.CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED;
+ } else if (Objects.equals(dataDir, packageInfo.getDeviceEncryptedDataDirFile())) {
+ flags |= Context.CONTEXT_STORAGE_DEVICE_ENCRYPTED;
+ } else {
+ throw new IllegalStateException("Storage location " + dataDir
+ + " doesn't match either credential or device encrypted storage");
+ }
+ }
+
mMainThread = mainThread;
mActivityToken = activityToken;
- mRestricted = restricted;
+ mFlags = flags;
if (user == null) {
user = Process.myUserHandle();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 471750e..6b900a8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2325,8 +2325,8 @@
* with that alias already exists, it will be overwritten.
* @return {@code true} if the keys were installed, {@code false} otherwise.
*/
- public boolean installKeyPair(@Nullable ComponentName admin, PrivateKey privKey, Certificate cert,
- String alias) {
+ public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
+ @NonNull Certificate cert, @NonNull String alias) {
try {
final byte[] pemCert = Credentials.convertToPem(cert);
final byte[] pkcs8Key = KeyFactory.getInstance(privKey.getAlgorithm())
@@ -2343,6 +2343,24 @@
}
/**
+ * Called by a device or profile owner to remove all user credentials installed under a given
+ * alias.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+ * {@code null} if calling from a delegated certificate installer.
+ * @param alias The private key alias under which the certificate is installed.
+ * @return {@code true} if the keys were both removed, {@code false} otherwise.
+ */
+ public boolean removeKeyPair(@Nullable ComponentName admin, @NonNull String alias) {
+ try {
+ return mService.removeKeyPair(admin, alias);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ return false;
+ }
+
+ /**
* @return the alias of a given CA certificate in the certificate store, or {@code null} if it
* doesn't exist.
*/
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 6b4567c..1708ee3 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -135,6 +135,7 @@
void enforceCanManageCaCerts(in ComponentName admin);
boolean installKeyPair(in ComponentName who, in byte[] privKeyBuffer, in byte[] certBuffer, String alias);
+ boolean removeKeyPair(in ComponentName who, String alias);
void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback);
void setCertInstallerPackage(in ComponentName who, String installerPackage);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4c7e853..1f7fd9d 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -771,27 +771,6 @@
public abstract File getFilesDir();
/**
- * Return the filesystem directory for storing device-encrypted private app
- * data. Files stored in this location are typically encrypted with a key
- * tied to the physical device, and they can be accessed whenever the device
- * has booted successfully, both <em>before and after</em> the user has
- * entered their credentials (such as a lock pattern or PIN).
- */
- public abstract File getDeviceEncryptedFilesDir();
-
- /**
- * Return the filesystem directory for storing credential-encrypted private
- * app data. Files stored in this location are typically encrypted with a
- * key tied to user credentials, and they can be accessed
- * <em>only after</em> the user has entered their credentials (such as a
- * lock pattern or PIN).
- *
- * @hide
- */
- @SystemApi
- public abstract File getCredentialEncryptedFilesDir();
-
- /**
* Returns the absolute path to the directory on the filesystem similar to
* {@link #getFilesDir()}. The difference is that files placed under this
* directory will be excluded from automatic backup to remote storage. See
@@ -3886,6 +3865,26 @@
public static final int CONTEXT_RESTRICTED = 0x00000004;
/**
+ * Flag for use with {@link #createPackageContext}: point all file APIs at
+ * device-encrypted storage.
+ *
+ * @hide
+ */
+ public static final int CONTEXT_STORAGE_DEVICE_ENCRYPTED = 0x00000008;
+
+ /**
+ * Flag for use with {@link #createPackageContext}: point all file APIs at
+ * credential-encrypted storage.
+ *
+ * @hide
+ */
+ public static final int CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED = 0x00000010;
+
+ /** {@hide} */
+ public static final int CONTEXT_STORAGE_MASK = CONTEXT_STORAGE_DEVICE_ENCRYPTED
+ | CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED;
+
+ /**
* @hide Used to indicate we should tell the activity manager about the process
* loading this code.
*/
@@ -3985,6 +3984,42 @@
public abstract Context createDisplayContext(@NonNull Display display);
/**
+ * Return a new Context object for the current Context but whose storage
+ * APIs are backed by device-encrypted storage.
+ * <p>
+ * Data stored in device-encrypted storage is typically encrypted with a
+ * key tied to the physical device, and they can be accessed whenever the
+ * device has booted successfully, both <em>before and after</em> the user
+ * has entered their credentials (such as a lock pattern or PIN).
+ * <p>
+ * Each call to this method returns a new instance of a Context object;
+ * Context objects are not shared, however common state (ClassLoader, other
+ * Resources for the same configuration) may be so the Context itself can be
+ * fairly lightweight.
+ *
+ * @see #isDeviceEncrypted()
+ */
+ public abstract Context createDeviceEncryptedContext(Context context);
+
+ /**
+ * Return a new Context object for the current Context but whose storage
+ * APIs are backed by credential-encrypted storage.
+ * <p>
+ * Data stored in credential-encrypted storage is typically encrypted with a
+ * key tied to user credentials, and they can be accessed
+ * <em>only after</em> the user has entered their credentials (such as a
+ * lock pattern or PIN).
+ * <p>
+ * Each call to this method returns a new instance of a Context object;
+ * Context objects are not shared, however common state (ClassLoader, other
+ * Resources for the same configuration) may be so the Context itself can be
+ * fairly lightweight.
+ *
+ * @see #isCredentialEncrypted()
+ */
+ public abstract Context createCredentialEncryptedContext(Context context);
+
+ /**
* Gets the display adjustments holder for this context. This information
* is provided on a per-application or activity basis and is used to simulate lower density
* display metrics for legacy applications and restricted screen sizes.
@@ -4005,4 +4040,20 @@
public boolean isRestricted() {
return false;
}
+
+ /**
+ * Indicates if the storage APIs of this Context are backed by
+ * device-encrypted storage.
+ *
+ * @see #createDeviceEncryptedContext(Context)
+ */
+ public abstract boolean isDeviceEncrypted();
+
+ /**
+ * Indicates if the storage APIs of this Context are backed by
+ * credential-encrypted storage.
+ *
+ * @see #createCredentialEncryptedContext(Context)
+ */
+ public abstract boolean isCredentialEncrypted();
}
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index f5e9159..73d0ddc 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -208,18 +208,6 @@
}
@Override
- public File getDeviceEncryptedFilesDir() {
- return mBase.getDeviceEncryptedFilesDir();
- }
-
- /** {@hide} */
- @SystemApi
- @Override
- public File getCredentialEncryptedFilesDir() {
- return mBase.getCredentialEncryptedFilesDir();
- }
-
- @Override
public File getNoBackupFilesDir() {
return mBase.getNoBackupFilesDir();
}
@@ -802,4 +790,24 @@
public DisplayAdjustments getDisplayAdjustments(int displayId) {
return mBase.getDisplayAdjustments(displayId);
}
+
+ @Override
+ public Context createDeviceEncryptedContext(Context context) {
+ return mBase.createDeviceEncryptedContext(context);
+ }
+
+ @Override
+ public Context createCredentialEncryptedContext(Context context) {
+ return mBase.createCredentialEncryptedContext(context);
+ }
+
+ @Override
+ public boolean isDeviceEncrypted() {
+ return mBase.isDeviceEncrypted();
+ }
+
+ @Override
+ public boolean isCredentialEncrypted() {
+ return mBase.isCredentialEncrypted();
+ }
}
diff --git a/core/java/android/content/pm/ApplicationInfo.aidl b/core/java/android/content/pm/ApplicationInfo.aidl
index 006d1bd..59b0a89 100644
--- a/core/java/android/content/pm/ApplicationInfo.aidl
+++ b/core/java/android/content/pm/ApplicationInfo.aidl
@@ -1,17 +1,17 @@
-/* //device/java/android/android/view/WindowManager.aidl
+/*
**
** Copyright 2007, 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
+** 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,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** 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.
*/
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 545478c..4c5e766 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -22,7 +22,6 @@
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.text.TextUtils;
@@ -383,13 +382,6 @@
public static final int FLAG_HARDWARE_ACCELERATED = 1<<29;
/**
- * Value for {@link #flags}: {@code true} if the application is blocked via restrictions
- * and for most purposes is considered as not installed.
- * {@hide}
- */
- public static final int FLAG_EPHEMERAL = 1<<30;
-
- /**
* Value for {@link #flags}: true if code from this application will need to be
* loaded into other applications' processes. On devices that support multiple
* instruction sets, this implies the code might be loaded into a process that's
@@ -462,8 +454,8 @@
public static final int PRIVATE_FLAG_PRIVILEGED = 1<<3;
/**
- * Value for {@link #flags}: {@code true} if the application has any IntentFiler with some
- * data URI using HTTP or HTTPS with an associated VIEW action.
+ * Value for {@link #privateFlags}: {@code true} if the application has any IntentFiler
+ * with some data URI using HTTP or HTTPS with an associated VIEW action.
*
* {@hide}
*/
@@ -494,6 +486,13 @@
public static final int PRIVATE_FLAG_AUTOPLAY = 1 << 7;
/**
+ * Value for {@link #flags}: {@code true} if the application is blocked via restrictions
+ * and for most purposes is considered as not installed.
+ * {@hide}
+ */
+ public static final int PRIVATE_FLAG_EPHEMERAL = 1<<8;
+
+ /**
* When set, at least one component inside this application is encryption aware.
*
* @hide
@@ -591,13 +590,21 @@
public String[] sharedLibraryFiles;
/**
- * Full path to a directory assigned to the package for its persistent data.
+ * Full path to the default directory assigned to the package for its
+ * persistent data.
*/
public String dataDir;
- /** {@hide} */
+ /**
+ * Full path to the device-encrypted directory assigned to the package for
+ * its persistent data.
+ */
public String deviceEncryptedDataDir;
- /** {@hide} */
+
+ /**
+ * Full path to the credential-encrypted directory assigned to the package
+ * for its persistent data.
+ */
public String credentialEncryptedDataDir;
/**
@@ -1083,6 +1090,13 @@
/**
* @hide
*/
+ public boolean isEphemeralApp() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_EPHEMERAL) != 0;
+ }
+
+ /**
+ * @hide
+ */
@Override protected ApplicationInfo getApplicationInfo() {
return this;
}
diff --git a/core/java/android/content/pm/EphemeralApplicationInfo.aidl b/core/java/android/content/pm/EphemeralApplicationInfo.aidl
new file mode 100644
index 0000000..5aaae78
--- /dev/null
+++ b/core/java/android/content/pm/EphemeralApplicationInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015 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.content.pm;
+
+parcelable EphemeralApplicationInfo;
diff --git a/core/java/android/content/pm/EphemeralApplicationInfo.java b/core/java/android/content/pm/EphemeralApplicationInfo.java
new file mode 100644
index 0000000..87663f1
--- /dev/null
+++ b/core/java/android/content/pm/EphemeralApplicationInfo.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class represents the state of an ephemeral app.
+ *
+ * @hide
+ */
+public final class EphemeralApplicationInfo implements Parcelable {
+ private final ApplicationInfo mApplicationInfo;
+
+ private final String mPackageName;
+ private final CharSequence mLabelText;
+
+ private final String[] mRequestedPermissions;
+ private final String[] mGrantedPermissions;
+
+ public EphemeralApplicationInfo(ApplicationInfo appInfo,
+ String[] requestedPermissions, String[] grantedPermissions) {
+ mApplicationInfo = appInfo;
+ mPackageName = null;
+ mLabelText = null;
+ mRequestedPermissions = requestedPermissions;
+ mGrantedPermissions = grantedPermissions;
+ }
+
+ public EphemeralApplicationInfo(String packageName, CharSequence label,
+ String[] requestedPermissions, String[] grantedPermissions) {
+ mApplicationInfo = null;
+ mPackageName = packageName;
+ mLabelText = label;
+ mRequestedPermissions = requestedPermissions;
+ mGrantedPermissions = grantedPermissions;
+ }
+
+ private EphemeralApplicationInfo(Parcel parcel) {
+ mPackageName = parcel.readString();
+ mLabelText = parcel.readCharSequence();
+ mRequestedPermissions = parcel.readStringArray();
+ mGrantedPermissions = parcel.createStringArray();
+ mApplicationInfo = parcel.readParcelable(null);
+ }
+
+ public @NonNull String getPackageName() {
+ if (mApplicationInfo != null) {
+ return mApplicationInfo.packageName;
+ }
+ return mPackageName;
+ }
+
+ public @NonNull CharSequence loadLabel(@NonNull PackageManager packageManager) {
+ if (mApplicationInfo != null) {
+ return mApplicationInfo.loadLabel(packageManager);
+ }
+ return mLabelText;
+ }
+
+ public @NonNull Drawable loadIcon(@NonNull PackageManager packageManager) {
+ if (mApplicationInfo != null) {
+ return mApplicationInfo.loadIcon(packageManager);
+ }
+ return packageManager.getEphemeralApplicationIcon(mPackageName);
+ }
+
+ public @Nullable String[] getRequestedPermissions() {
+ return mRequestedPermissions;
+ }
+
+ public @Nullable String[] getGrantedPermissions() {
+ return mGrantedPermissions;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mPackageName);
+ parcel.writeCharSequence(mLabelText);
+ parcel.writeStringArray(mRequestedPermissions);
+ parcel.writeStringArray(mGrantedPermissions);
+ parcel.writeParcelable(mApplicationInfo, flags);
+ }
+
+ public static final Creator<EphemeralApplicationInfo> CREATOR =
+ new Creator<EphemeralApplicationInfo>() {
+ @Override
+ public EphemeralApplicationInfo createFromParcel(Parcel parcel) {
+ return new EphemeralApplicationInfo(parcel);
+ }
+
+ @Override
+ public EphemeralApplicationInfo[] newArray(int size) {
+ return new EphemeralApplicationInfo[0];
+ }
+ };
+}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index b9a42eb..b947a2b 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -23,6 +23,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ContainerEncryptionParams;
+import android.content.pm.EphemeralApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageInstaller;
@@ -47,6 +48,7 @@
import android.content.pm.UserInfo;
import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
+import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
@@ -513,4 +515,10 @@
boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId);
String getPermissionControllerPackageName();
+
+ ParceledListSlice getEphemeralApplications(int userId);
+ byte[] getEphemeralApplicationCookie(String packageName, int userId);
+ boolean setEphemeralApplicationCookie(String packageName, in byte[] cookie, int userId);
+ Bitmap getEphemeralApplicationIcon(String packageName, int userId);
+ boolean isEphemeralApplication(String packageName, int userId);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9b80a16..a822150 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -47,8 +47,10 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.storage.VolumeInfo;
+import android.provider.Settings;
import android.util.AndroidException;
+import android.util.Log;
import com.android.internal.util.ArrayUtils;
import java.io.File;
@@ -2860,6 +2862,83 @@
public abstract List<ApplicationInfo> getInstalledApplications(int flags);
/**
+ * Gets the ephemeral applications the user recently used. Requires
+ * holding "android.permission.ACCESS_EPHEMERAL_APPS".
+ *
+ * @return The ephemeral app list.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_EPHEMERAL_APPS)
+ public abstract List<EphemeralApplicationInfo> getEphemeralApplications();
+
+ /**
+ * Gets the icon for an ephemeral application.
+ *
+ * @param packageName The app package name.
+ *
+ * @hide
+ */
+ public abstract Drawable getEphemeralApplicationIcon(String packageName);
+
+ /**
+ * Gets whether the caller is an ephemeral app.
+ *
+ * @return Whether caller is an ephemeral app.
+ *
+ * @see #setEphemeralCookie(byte[])
+ * @see #getEphemeralCookie()
+ * @see #getEphemeralCookieMaxSizeBytes()
+ */
+ public abstract boolean isEphemeralApplication();
+
+ /**
+ * Gets the maximum size in bytes of the cookie data an ephemeral app
+ * can store on the device.
+ *
+ * @return The max cookie size in bytes.
+ *
+ * @see #isEphemeralApplication()
+ * @see #setEphemeralCookie(byte[])
+ * @see #getEphemeralCookie()
+ */
+ public abstract int getEphemeralCookieMaxSizeBytes();
+
+ /**
+ * Gets the ephemeral application cookie for this app. Non
+ * ephemeral apps and apps that were ephemeral but were upgraded
+ * to non-ephemeral can still access this API. For ephemeral apps
+ * this cooke is cached for some time after uninstall while for
+ * normal apps the cookie is deleted after the app is uninstalled.
+ * The cookie is always present while the app is installed.
+ *
+ * @return The cookie.
+ *
+ * @see #isEphemeralApplication()
+ * @see #setEphemeralCookie(byte[])
+ * @see #getEphemeralCookieMaxSizeBytes()
+ */
+ public abstract @NonNull byte[] getEphemeralCookie();
+
+ /**
+ * Sets the ephemeral application cookie for the calling app. Non
+ * ephemeral apps and apps that were ephemeral but were upgraded
+ * to non-ephemeral can still access this API. For ephemeral apps
+ * this cooke is cached for some time after uninstall while for
+ * normal apps the cookie is deleted after the app is uninstalled.
+ * The cookie is always present while the app is installed. The
+ * cookie size is limited by {@link #getEphemeralCookieMaxSizeBytes()}.
+ *
+ * @param cookie The cookie data.
+ * @return True if the cookie was set.
+ *
+ * @see #isEphemeralApplication()
+ * @see #getEphemeralCookieMaxSizeBytes()
+ * @see #getEphemeralCookie();
+ */
+ public abstract boolean setEphemeralCookie(@NonNull byte[] cookie);
+
+ /**
* Get a list of shared libraries that are available on the
* system.
*
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0307108..b79b6b6 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1470,6 +1470,10 @@
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
}
+ if ((flags & PARSE_IS_EPHEMERAL) != 0) {
+ pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_EPHEMERAL;
+ }
+
// Resource boolean are -1, so 1 means we don't know the value.
int supportsSmallScreens = 1;
int supportsNormalScreens = 1;
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 6e09595..e3050fe 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -78,6 +78,12 @@
public static final int FLAG_QUIET_MODE = 0x00000080;
+ /**
+ * Indicates that this user is ephemeral. I.e. the user will be removed after leaving
+ * the foreground.
+ */
+ public static final int FLAG_EPHEMERAL = 0x00000100;
+
public static final int NO_PROFILE_GROUP_ID = UserHandle.USER_NULL;
public int id;
@@ -134,6 +140,11 @@
public boolean isQuietModeEnabled() {
return (flags & FLAG_QUIET_MODE) == FLAG_QUIET_MODE;
}
+
+ public boolean isEphemeral() {
+ return (flags & FLAG_EPHEMERAL) == FLAG_EPHEMERAL;
+ }
+
/**
* Returns true if the user is a split system user.
* <p>If {@link UserManager#isSplitSystemUser split system user mode} is not enabled,
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 7b3dde4..42344a2 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -2421,75 +2421,93 @@
@Nullable
Drawable loadDrawable(TypedValue value, int id, Theme theme) throws NotFoundException {
- if (TRACE_FOR_PRELOAD) {
- // Log only framework resources
- if ((id >>> 24) == 0x1) {
- final String name = getResourceName(id);
- if (name != null) {
- Log.d("PreloadDrawable", name);
+ try {
+ if (TRACE_FOR_PRELOAD) {
+ // Log only framework resources
+ if ((id >>> 24) == 0x1) {
+ final String name = getResourceName(id);
+ if (name != null) {
+ Log.d("PreloadDrawable", name);
+ }
}
}
- }
- final boolean isColorDrawable;
- final DrawableCache caches;
- final long key;
- if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
- && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
- isColorDrawable = true;
- caches = mColorDrawableCache;
- key = value.data;
- } else {
- isColorDrawable = false;
- caches = mDrawableCache;
- key = (((long) value.assetCookie) << 32) | value.data;
- }
-
- // First, check whether we have a cached version of this drawable
- // that was inflated against the specified theme.
- if (!mPreloading) {
- final Drawable cachedDrawable = caches.getInstance(key, theme);
- if (cachedDrawable != null) {
- return cachedDrawable;
+ final boolean isColorDrawable;
+ final DrawableCache caches;
+ final long key;
+ if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
+ && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+ isColorDrawable = true;
+ caches = mColorDrawableCache;
+ key = value.data;
+ } else {
+ isColorDrawable = false;
+ caches = mDrawableCache;
+ key = (((long) value.assetCookie) << 32) | value.data;
}
- }
- // Next, check preloaded drawables. These may contain unresolved theme
- // attributes.
- final ConstantState cs;
- if (isColorDrawable) {
- cs = sPreloadedColorDrawables.get(key);
- } else {
- cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
- }
+ // First, check whether we have a cached version of this drawable
+ // that was inflated against the specified theme.
+ if (!mPreloading) {
+ final Drawable cachedDrawable = caches.getInstance(key, theme);
+ if (cachedDrawable != null) {
+ return cachedDrawable;
+ }
+ }
- Drawable dr;
- if (cs != null) {
- dr = cs.newDrawable(this);
- } else if (isColorDrawable) {
- dr = new ColorDrawable(value.data);
- } else {
- dr = loadDrawableForCookie(value, id, null);
- }
+ // Next, check preloaded drawables. These may contain unresolved theme
+ // attributes.
+ final ConstantState cs;
+ if (isColorDrawable) {
+ cs = sPreloadedColorDrawables.get(key);
+ } else {
+ cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
+ }
- // Determine if the drawable has unresolved theme attributes. If it
- // does, we'll need to apply a theme and store it in a theme-specific
- // cache.
- final boolean canApplyTheme = dr != null && dr.canApplyTheme();
- if (canApplyTheme && theme != null) {
- dr = dr.mutate();
- dr.applyTheme(theme);
- dr.clearMutated();
- }
+ Drawable dr;
+ if (cs != null) {
+ dr = cs.newDrawable(this);
+ } else if (isColorDrawable) {
+ dr = new ColorDrawable(value.data);
+ } else {
+ dr = loadDrawableForCookie(value, id, null);
+ }
- // If we were able to obtain a drawable, store it in the appropriate
- // cache: preload, not themed, null theme, or theme-specific.
- if (dr != null) {
- dr.setChangingConfigurations(value.changingConfigurations);
- cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
- }
+ // Determine if the drawable has unresolved theme attributes. If it
+ // does, we'll need to apply a theme and store it in a theme-specific
+ // cache.
+ final boolean canApplyTheme = dr != null && dr.canApplyTheme();
+ if (canApplyTheme && theme != null) {
+ dr = dr.mutate();
+ dr.applyTheme(theme);
+ dr.clearMutated();
+ }
- return dr;
+ // If we were able to obtain a drawable, store it in the appropriate
+ // cache: preload, not themed, null theme, or theme-specific.
+ if (dr != null) {
+ dr.setChangingConfigurations(value.changingConfigurations);
+ cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
+ }
+
+ return dr;
+ } catch (Exception e) {
+ String name;
+ try {
+ name = getResourceName(id);
+ } catch (NotFoundException e2) {
+ name = "(missing name)";
+ }
+
+ // The target drawable might fail to load for any number of
+ // reasons, but we always want to include the resource name.
+ // Since the client already expects this method to throw a
+ // NotFoundException, just throw one of those.
+ final NotFoundException nfe = new NotFoundException("Drawable " + name
+ + " with resource ID #0x" + Integer.toHexString(id), e);
+ nfe.setStackTrace(new StackTraceElement[0]);
+ throw nfe;
+ }
}
private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches,
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 4af89c9..9ee6228 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -48,12 +48,15 @@
UserInfo getProfileParent(int userHandle);
boolean isSameProfileGroup(int userId, int otherUserId);
UserInfo getUserInfo(int userHandle);
+ String getUserAccount(int userId);
+ void setUserAccount(int userId, String accountName);
long getUserCreationTime(int userHandle);
boolean isRestricted();
boolean canHaveRestrictedProfile(int userId);
int getUserSerialNumber(int userHandle);
int getUserHandle(int userSerialNumber);
Bundle getUserRestrictions(int userHandle);
+ boolean hasBaseUserRestriction(String restrictionKey, int userHandle);
boolean hasUserRestriction(in String restrictionKey, int userHandle);
void setUserRestriction(String key, boolean value, int userId);
void setApplicationRestrictions(in String packageName, in Bundle restrictions,
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 8d035b7..9b68f90 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -236,6 +236,7 @@
private static final int VAL_DOUBLEARRAY = 28;
// The initial int32 in a Binder call's reply Parcel header:
+ // Keep these in sync with libbinder's binder/Status.h.
private static final int EX_SECURITY = -1;
private static final int EX_BAD_PARCELABLE = -2;
private static final int EX_ILLEGAL_ARGUMENT = -3;
@@ -243,7 +244,11 @@
private static final int EX_ILLEGAL_STATE = -5;
private static final int EX_NETWORK_MAIN_THREAD = -6;
private static final int EX_UNSUPPORTED_OPERATION = -7;
+ private static final int EX_SERVICE_SPECIFIC = -8;
private static final int EX_HAS_REPLY_HEADER = -128; // special; see below
+ // EX_TRANSACTION_FAILED is used exclusively in native code.
+ // see libbinder's binder/Status.h
+ private static final int EX_TRANSACTION_FAILED = -129;
private static native int nativeDataSize(long nativePtr);
private static native int nativeDataAvail(long nativePtr);
@@ -1540,6 +1545,8 @@
code = EX_NETWORK_MAIN_THREAD;
} else if (e instanceof UnsupportedOperationException) {
code = EX_UNSUPPORTED_OPERATION;
+ } else if (e instanceof ServiceSpecificException) {
+ code = EX_SERVICE_SPECIFIC;
}
writeInt(code);
StrictMode.clearGatheredViolations();
@@ -1550,6 +1557,9 @@
throw new RuntimeException(e);
}
writeString(e.getMessage());
+ if (e instanceof ServiceSpecificException) {
+ writeInt(((ServiceSpecificException)e).errorCode);
+ }
}
/**
@@ -1660,6 +1670,8 @@
throw new NetworkOnMainThreadException();
case EX_UNSUPPORTED_OPERATION:
throw new UnsupportedOperationException(msg);
+ case EX_SERVICE_SPECIFIC:
+ throw new ServiceSpecificException(readInt(), msg);
}
throw new RuntimeException("Unknown exception code: " + code
+ " msg " + msg);
diff --git a/core/java/android/os/ServiceSpecificException.java b/core/java/android/os/ServiceSpecificException.java
new file mode 100644
index 0000000..20f237a5
--- /dev/null
+++ b/core/java/android/os/ServiceSpecificException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 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.os;
+
+/**
+ * An exception specific to a service.
+ *
+ * <p>This exception includes an error code specific to the throwing
+ * service. This is mostly used by system services to indicate
+ * domain specific error conditions.
+ *
+ * @hide
+ */
+public class ServiceSpecificException extends RuntimeException {
+ public final int errorCode;
+
+ ServiceSpecificException(int errorCode, String message) {
+ super(message);
+ this.errorCode = errorCode;
+ }
+
+ ServiceSpecificException(int errorCode) {
+ this.errorCode = errorCode;
+ }
+}
+
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 4781a6c..1346a39 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -15,8 +15,10 @@
*/
package android.os;
+import android.Manifest;
import android.accounts.AccountManager;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
@@ -721,6 +723,25 @@
}
/**
+ * Checks if the calling app is running as an ephemeral user.
+ *
+ * @return whether the caller is an ephemeral user.
+ * @hide
+ */
+ public boolean isEphemeralUser() {
+ return isUserEphemeral(UserHandle.myUserId());
+ }
+
+ /**
+ * Returns whether the specified user is ephemeral.
+ * @hide
+ */
+ public boolean isUserEphemeral(int userId) {
+ final UserInfo user = getUserInfo(userId);
+ return user != null && user.isEphemeral();
+ }
+
+ /**
* Return whether the given user is actively running. This means that
* the user is in the "started" state, not "stopped" -- it is currently
* allowed to run code through scheduled alarms, receiving broadcasts,
@@ -826,6 +847,24 @@
}
}
+ /**
+ * @hide
+ * Returns whether the given user has been disallowed from performing certain actions
+ * or setting certain settings through UserManager. This method disregards restrictions
+ * set by device policy.
+ * @param restrictionKey the string key representing the restriction
+ * @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
+ */
+ public boolean hasBaseUserRestriction(String restrictionKey, UserHandle userHandle) {
+ try {
+ return mService.hasBaseUserRestriction(restrictionKey, userHandle.getIdentifier());
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not get base user restrictions for user " +
+ userHandle.getIdentifier(), re);
+ return false;
+ }
+ }
+
/**
* This will no longer work. Device owners and profile owners should use
* {@link DevicePolicyManager#addUserRestriction(ComponentName, String)} instead.
@@ -1084,6 +1123,39 @@
}
/**
+ * @return the user's account name, null if not found.
+ * @hide
+ */
+ @RequiresPermission( allOf = {
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ Manifest.permission.MANAGE_USERS
+ })
+ public @Nullable String getUserAccount(int userHandle) {
+ try {
+ return mService.getUserAccount(userHandle);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not get user account", re);
+ return null;
+ }
+ }
+
+ /**
+ * Set account name for the given user.
+ * @hide
+ */
+ @RequiresPermission( allOf = {
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ Manifest.permission.MANAGE_USERS
+ })
+ public void setUserAccount(int userHandle, @Nullable String accountName) {
+ try {
+ mService.setUserAccount(userHandle, accountName);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not set user account", re);
+ }
+ }
+
+ /**
* Returns information for Primary user.
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
*
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index c6510f0..9b3f02d 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -25,8 +25,6 @@
import android.os.Parcelable;
import android.os.RemoteException;
-import java.io.FileDescriptor;
-
/**
* WARNING! Update IMountService.h and IMountService.cpp if you change this
* file. In particular, the ordering of the methods below must match the
@@ -1202,13 +1200,15 @@
}
@Override
- public void createUserKey(int userId, int serialNumber) throws RemoteException {
+ public void createUserKey(int userId, int serialNumber, boolean ephemeral)
+ throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(userId);
_data.writeInt(serialNumber);
+ _data.writeInt(ephemeral ? 1 : 0);
mRemote.transact(Stub.TRANSACTION_createUserKey, _data, _reply, 0);
_reply.readException();
} finally {
@@ -1283,7 +1283,8 @@
}
@Override
- public void prepareUserStorage(String volumeUuid, int userId, int serialNumber)
+ public void prepareUserStorage(
+ String volumeUuid, int userId, int serialNumber, boolean ephemeral)
throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
@@ -1292,6 +1293,7 @@
_data.writeString(volumeUuid);
_data.writeInt(userId);
_data.writeInt(serialNumber);
+ _data.writeInt(ephemeral ? 1 : 0);
mRemote.transact(Stub.TRANSACTION_prepareUserStorage, _data, _reply, 0);
_reply.readException();
} finally {
@@ -2012,7 +2014,8 @@
data.enforceInterface(DESCRIPTOR);
int userId = data.readInt();
int serialNumber = data.readInt();
- createUserKey(userId, serialNumber);
+ boolean ephemeral = data.readInt() != 0;
+ createUserKey(userId, serialNumber, ephemeral);
reply.writeNoException();
return true;
}
@@ -2052,7 +2055,8 @@
String volumeUuid = data.readString();
int userId = data.readInt();
int serialNumber = data.readInt();
- prepareUserStorage(volumeUuid, userId, serialNumber);
+ boolean ephemeral = data.readInt() != 0;
+ prepareUserStorage(volumeUuid, userId, serialNumber, ephemeral);
reply.writeNoException();
return true;
}
@@ -2376,15 +2380,16 @@
public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
throws RemoteException;
- public void createUserKey(int userId, int serialNumber) throws RemoteException;
+ public void createUserKey(int userId, int serialNumber, boolean ephemeral)
+ throws RemoteException;
public void destroyUserKey(int userId) throws RemoteException;
public void unlockUserKey(int userId, int serialNumber, byte[] token) throws RemoteException;
public void lockUserKey(int userId) throws RemoteException;
public boolean isUserKeyUnlocked(int userId) throws RemoteException;
- public void prepareUserStorage(String volumeUuid, int userId, int serialNumber)
- throws RemoteException;
+ public void prepareUserStorage(String volumeUuid, int userId, int serialNumber,
+ boolean ephemeral) throws RemoteException;
public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException;
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 7f36a98..beda169 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -966,9 +966,9 @@
}
/** {@hide} */
- public void createUserKey(int userId, int serialNumber) {
+ public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
try {
- mMountService.createUserKey(userId, serialNumber);
+ mMountService.createUserKey(userId, serialNumber, ephemeral);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -1002,9 +1002,10 @@
}
/** {@hide} */
- public void prepareUserStorage(String volumeUuid, int userId, int serialNumber) {
+ public void prepareUserStorage(
+ String volumeUuid, int userId, int serialNumber, boolean ephemeral) {
try {
- mMountService.prepareUserStorage(volumeUuid, userId, serialNumber);
+ mMountService.prepareUserStorage(volumeUuid, userId, serialNumber, ephemeral);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index dad3c67..521aa3c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7792,6 +7792,24 @@
public static final String LTE_SERVICE_FORCED = "lte_service_forced";
/**
+ * Ephemeral app cookie max size in bytes.
+ * <p>
+ * Type: int
+ * @hide
+ */
+ public static final String EPHEMERAL_COOKIE_MAX_SIZE_BYTES =
+ "ephemeral_cookie_max_size_bytes";
+
+ /**
+ * The duration for caching uninstalled ephemeral apps.
+ * <p>
+ * Type: long
+ * @hide
+ */
+ public static final String UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS =
+ "uninstalled_ephemeral_app_cache_duration_millis";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index bae51d3..46b9a46 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -256,6 +256,15 @@
public static final int SOURCE_TOUCH_NAVIGATION = 0x00200000 | SOURCE_CLASS_NONE;
/**
+ * The input source is a rotating encoder device whose motions should be interpreted as akin to
+ * those of a scroll wheel.
+ *
+ * @see #SOURCE_CLASS_NONE
+ * {@hide}
+ */
+ public static final int SOURCE_ROTARY_ENCODER = 0x00400000 | SOURCE_CLASS_NONE;
+
+ /**
* The input source is a joystick.
* (It may also be a {@link #SOURCE_GAMEPAD}).
*
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 6026d04..527d7e5 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -962,6 +962,22 @@
public static final int AXIS_TILT = 25;
/**
+ * Axis constant: Generic scroll axis of a motion event.
+ * <p>
+ * <ul>
+ * <li>Reports the relative movement of the generic scrolling device.
+ * </ul>
+ * </p><p>
+ * This axis should be used for scroll events that are neither strictly vertical nor horizontal.
+ * A good example would be the rotation of a rotary encoder input device.
+ * </p>
+ *
+ * @see #getAxisValue(int, int)
+ * {@hide}
+ */
+ public static final int AXIS_SCROLL = 26;
+
+ /**
* Axis constant: Generic 1 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
@@ -1171,6 +1187,7 @@
names.append(AXIS_BRAKE, "AXIS_BRAKE");
names.append(AXIS_DISTANCE, "AXIS_DISTANCE");
names.append(AXIS_TILT, "AXIS_TILT");
+ names.append(AXIS_SCROLL, "AXIS_SCROLL");
names.append(AXIS_GENERIC_1, "AXIS_GENERIC_1");
names.append(AXIS_GENERIC_2, "AXIS_GENERIC_2");
names.append(AXIS_GENERIC_3, "AXIS_GENERIC_3");
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 7e45bbb..b1a8479 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -431,6 +431,11 @@
public int getLidState();
/**
+ * Lock the device now.
+ */
+ public void lockDeviceNow();
+
+ /**
* Returns a code that descripbes whether the camera lens is covered or not.
*/
public int getCameraLensCoverState();
diff --git a/core/java/com/android/internal/logging/MetricsConstants.java b/core/java/com/android/internal/logging/MetricsConstants.java
index bb4ca3d..37bf71c 100644
--- a/core/java/com/android/internal/logging/MetricsConstants.java
+++ b/core/java/com/android/internal/logging/MetricsConstants.java
@@ -281,6 +281,7 @@
public static final int ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE = 255;
public static final int ACTION_WIGGLE_CAMERA_GESTURE = 256;
public static final int QS_WORKMODE = 257;
+ public static final int BACKGROUND_CHECK_SUMMARY = 258;
// These constants must match those in the analytic pipeline, do not edit.
// Add temporary values to the top of MetricsLogger instead.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4fda222..5251b20 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2777,6 +2777,12 @@
confirmation UI for full backup/restore -->
<uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
+
+ <!-- Allows the holder to access the ephemeral applications on the device.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_EPHEMERAL_APPS"
+ android:protectionLevel="signature" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index decb1ef..97c0e07 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -673,6 +673,10 @@
closed. The default is 0. -->
<integer name="config_lidNavigationAccessibility">0</integer>
+ <!-- Indicate whether closing the lid causes the lockscreen to appear.
+ The default is false. -->
+ <bool name="config_lidControlsScreenLock">false</bool>
+
<!-- Indicate whether closing the lid causes the device to go to sleep and opening
it causes the device to wake up.
The default is false. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d92adc2..1de4c45 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1480,6 +1480,7 @@
<java-symbol type="bool" name="config_enableLockScreenRotation" />
<java-symbol type="bool" name="config_enableLockScreenTranslucentDecor" />
<java-symbol type="bool" name="config_enableTranslucentDecor" />
+ <java-symbol type="bool" name="config_lidControlsScreenLock" />
<java-symbol type="bool" name="config_lidControlsSleep" />
<java-symbol type="bool" name="config_lockDayNightMode" />
<java-symbol type="bool" name="config_lockUiMode" />
diff --git a/core/tests/benchmarks/Android.mk b/core/tests/benchmarks/Android.mk
index 26a44a6..25181b5 100644
--- a/core/tests/benchmarks/Android.mk
+++ b/core/tests/benchmarks/Android.mk
@@ -25,7 +25,6 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src/)
LOCAL_JAVA_LIBRARIES := \
- caliper-api-target \
- framework
+ caliper-api-target
include $(BUILD_JAVA_LIBRARY)
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 1b97b65..0e66374 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -344,7 +344,7 @@
<family>
<font weight="400" style="normal">NanumGothic.ttf</font>
</family>
- <family>
+ <family lang="und-Qaae">
<font weight="400" style="normal">NotoColorEmoji.ttf</font>
</family>
<family>
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index 971a3a2..d714ca8 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -153,7 +153,7 @@
updateStateFromTypedArray(a);
verifyRequiredAttributes(a);
} catch (XmlPullParserException e) {
- throw new RuntimeException(e);
+ rethrowAsRuntimeException(e);
} finally {
a.recycle();
}
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 4d2037b..daf2581 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -829,7 +829,7 @@
try {
updateStateFromTypedArray(a);
} catch (XmlPullParserException e) {
- throw new RuntimeException(e);
+ rethrowAsRuntimeException(e);
} finally {
a.recycle();
}
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index cdd336d..d925b6b 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -111,7 +111,7 @@
updateStateFromTypedArray(a);
verifyRequiredAttributes(a);
} catch (XmlPullParserException e) {
- throw new RuntimeException(e);
+ rethrowAsRuntimeException(e);
} finally {
a.recycle();
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 0ee877e..3d8437d 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1430,6 +1430,20 @@
}
/**
+ * Re-throws an exception as a {@link RuntimeException} with an empty stack
+ * trace to avoid cluttering the log. The original exception's stack trace
+ * will still be included.
+ *
+ * @param cause the exception to re-throw
+ * @throws RuntimeException
+ */
+ static void rethrowAsRuntimeException(Exception cause) throws RuntimeException {
+ final RuntimeException e = new RuntimeException(cause);
+ e.setStackTrace(new StackTraceElement[0]);
+ throw e;
+ }
+
+ /**
* Parses a {@link android.graphics.PorterDuff.Mode} from a tintMode
* attribute's enum value.
*
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 719297a..f9208cd 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -23,7 +23,6 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
@@ -1268,7 +1267,7 @@
try {
updateGradientDrawableGradient(t.getResources(), a);
} catch (XmlPullParserException e) {
- throw new RuntimeException(e);
+ rethrowAsRuntimeException(e);
} finally {
a.recycle();
}
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index 36d4272..d47cb56 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -124,7 +124,7 @@
updateStateFromTypedArray(a);
verifyRequiredAttributes(a);
} catch (XmlPullParserException e) {
- throw new RuntimeException(e);
+ rethrowAsRuntimeException(e);
} finally {
a.recycle();
}
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 4d51d63..bfbdfa5 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -510,7 +510,7 @@
try {
updateStateFromTypedArray(a);
} catch (XmlPullParserException e) {
- throw new RuntimeException(e);
+ rethrowAsRuntimeException(e);
} finally {
a.recycle();
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index aaab529..5213e10 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -40,7 +40,6 @@
import android.graphics.Rect;
import android.graphics.Shader;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
import java.io.IOException;
import java.util.Arrays;
@@ -505,7 +504,7 @@
updateStateFromTypedArray(a);
verifyRequiredAttributes(a);
} catch (XmlPullParserException e) {
- throw new RuntimeException(e);
+ rethrowAsRuntimeException(e);
} finally {
a.recycle();
}
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 1531ba2..78424e3 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -92,7 +92,7 @@
updateStateFromTypedArray(a);
verifyRequiredAttributes(a);
} catch (XmlPullParserException e) {
- throw new RuntimeException(e);
+ rethrowAsRuntimeException(e);
} finally {
a.recycle();
}
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index 330266f..51e143b 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -125,7 +125,7 @@
updateStateFromTypedArray(a);
verifyRequiredAttributes(a);
} catch (XmlPullParserException e) {
- throw new RuntimeException(e);
+ rethrowAsRuntimeException(e);
} finally {
a.recycle();
}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index f630055e..3761a99 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -511,7 +511,7 @@
state.mCacheDirty = true;
updateStateFromTypedArray(a);
} catch (XmlPullParserException e) {
- throw new RuntimeException(e);
+ rethrowAsRuntimeException(e);
} finally {
a.recycle();
}
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 20c94c5..cfcb4e0 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -33,6 +33,7 @@
// APIs used by DevicePolicyManager
boolean installKeyPair(in byte[] privateKey, in byte[] userCert, String alias);
+ boolean removeKeyPair(String alias);
// APIs used by Settings
boolean deleteCaCertificate(String alias);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 2e7c908..2d77c6a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -17,7 +17,6 @@
package com.android.documentsui.dirlist;
import static com.android.documentsui.Shared.DEBUG;
-import static com.android.documentsui.State.ACTION_CREATE;
import static com.android.documentsui.State.ACTION_MANAGE;
import static com.android.documentsui.State.MODE_GRID;
import static com.android.documentsui.State.MODE_LIST;
@@ -28,6 +27,7 @@
import static com.android.documentsui.model.DocumentInfo.getCursorString;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.Preconditions.checkState;
+import static com.google.common.base.Preconditions.checkArgument;
import android.app.Activity;
import android.app.ActivityManager;
@@ -89,6 +89,7 @@
import android.widget.TextView;
import com.android.documentsui.BaseActivity;
+import com.android.documentsui.BaseActivity.DocumentContext;
import com.android.documentsui.CopyService;
import com.android.documentsui.DirectoryLoader;
import com.android.documentsui.DirectoryResult;
@@ -101,20 +102,18 @@
import com.android.documentsui.MessageBar;
import com.android.documentsui.MimePredicate;
import com.android.documentsui.ProviderExecutor;
+import com.android.documentsui.ProviderExecutor.Preemptable;
import com.android.documentsui.R;
import com.android.documentsui.RecentLoader;
import com.android.documentsui.RecentsProvider;
+import com.android.documentsui.RecentsProvider.StateColumns;
import com.android.documentsui.RootCursorWrapper;
import com.android.documentsui.RootsCache;
import com.android.documentsui.Shared;
+import com.android.documentsui.Shared;
import com.android.documentsui.Snackbars;
import com.android.documentsui.State;
import com.android.documentsui.ThumbnailCache;
-import com.android.documentsui.BaseActivity.DocumentContext;
-import com.android.documentsui.ProviderExecutor.Preemptable;
-import com.android.documentsui.Shared;
-import com.android.documentsui.RecentsProvider.StateColumns;
-import com.android.documentsui.dirlist.MultiSelectManager.Callback;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
@@ -770,7 +769,9 @@
return true;
case R.id.menu_copy_to_clipboard:
- copySelectionToClipboard(selection);
+ if (!selection.isEmpty()) {
+ copySelectionToClipboard(selection);
+ }
return true;
case R.id.menu_select_all:
@@ -1359,11 +1360,14 @@
}
public void copySelectedToClipboard() {
- Selection sel = mSelectionManager.getSelection(new Selection());
- copySelectionToClipboard(sel);
+ Selection selection = mSelectionManager.getSelection(new Selection());
+ if (!selection.isEmpty()) {
+ copySelectionToClipboard(selection);
+ }
}
- void copySelectionToClipboard(Selection items) {
+ void copySelectionToClipboard(Selection selection) {
+ checkArgument(!selection.isEmpty());
new GetDocumentsTask() {
@Override
void onDocumentsReady(List<DocumentInfo> docs) {
@@ -1374,7 +1378,7 @@
R.plurals.clipboard_files_clipped, docs.size(), docs.size()),
Snackbar.LENGTH_SHORT).show();
}
- }.execute(items);
+ }.execute(selection);
}
public void pasteFromClipboard() {
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index a54d9ef..0e31cdf 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -49,6 +49,7 @@
import android.os.SystemProperties;
import android.service.notification.StatusBarNotification;
import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
import android.test.InstrumentationTestCase;
import android.util.Log;
@@ -68,8 +69,7 @@
* <li>asserts the extras received by the custom activity
* </ul>
* <p>
- * TODO: currently, these tests only work if the bug report sharing warning is disabled and the
- * device screen is unlocked.
+ * <strong>NOTE</strong>: these tests only work if the device is unlocked.
*/
public class BugreportReceiverTest extends InstrumentationTestCase {
@@ -99,6 +99,7 @@
mUiBot = new UiBot(UiDevice.getInstance(instrumentation), TIMEOUT);
mListener = ActionSendMultipleConsumerActivity.getListener(mContext);
cancelExistingNotifications();
+ BugreportPrefs.setWarningState(mContext, BugreportPrefs.STATE_HIDE);
}
public void testFullWorkflow() throws Exception {
@@ -132,6 +133,36 @@
// TODO: assert service is down
}
+ public void testBugreportFinished_withWarning() throws Exception {
+ // Explicitly shows the warning.
+ BugreportPrefs.setWarningState(mContext, BugreportPrefs.STATE_SHOW);
+
+ // Send notification and click on share.
+ createTextFile(PLAIN_TEXT_PATH, BUGREPORT_CONTENT);
+ Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
+ intent.putExtra(EXTRA_BUGREPORT, PLAIN_TEXT_PATH);
+ mContext.sendBroadcast(intent);
+ mUiBot.clickOnNotification(mContext.getString(R.string.bugreport_finished_title));
+
+ // Handle the warning
+ mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm));
+ // TODO: get ok and showMessageAgain from the dialog reference above
+ UiObject showMessageAgain =
+ mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm_repeat));
+ mUiBot.click(showMessageAgain, "show-message-again");
+ UiObject ok = mUiBot.getVisibleObject(mContext.getString(com.android.internal.R.string.ok));
+ mUiBot.click(ok, "ok");
+
+ // Share the bugreport.
+ mUiBot.chooseActivity(UI_NAME);
+ Bundle extras = mListener.getExtras();
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, null);
+
+ // Make sure it's hidden now.
+ int newState = BugreportPrefs.getWarningState(mContext, BugreportPrefs.STATE_UNKNOWN);
+ assertEquals("Didn't change state", BugreportPrefs.STATE_HIDE, newState);
+ }
+
public void testBugreportFinished_plainBugreportAndScreenshot() throws Exception {
createTextFile(PLAIN_TEXT_PATH, BUGREPORT_CONTENT);
createTextFile(SCREENSHOT_PATH, SCREENSHOT_CONTENT);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 2f79adf..2e65656 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -180,13 +180,6 @@
</intent-filter>
</receiver>
- <receiver android:name=".qs.tiles.HotspotTile$APChangedReceiver"
- androidprv:systemUserOnly="true">
- <intent-filter>
- <action android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
- </intent-filter>
- </receiver>
-
<activity android:name=".tuner.TunerActivity"
android:enabled="false"
android:icon="@drawable/tuner"
diff --git a/packages/SystemUI/res/drawable/quick_header_bg.xml b/packages/SystemUI/res/drawable/quick_header_bg.xml
new file mode 100644
index 0000000..d45d673
--- /dev/null
+++ b/packages/SystemUI/res/drawable/quick_header_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 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
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight" >
+ <item android:drawable="@color/system_primary_color"/>
+</ripple>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index cc35c51c..8124eb7 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -25,9 +25,10 @@
android:layout_gravity="@integer/notification_panel_layout_gravity"
android:paddingStart="@dimen/notification_side_padding"
android:paddingEnd="@dimen/notification_side_padding"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:baselineAligned="false"
- android:elevation="4dp"
- android:background="@drawable/notification_header_bg"
+ android:background="@drawable/quick_header_bg"
android:clickable="true"
android:focusable="true"
>
@@ -35,9 +36,11 @@
<com.android.systemui.qs.QuickQSPanel
android:id="@+id/quick_qs_panel"
android:background="#0000"
- android:layout_width="144dp"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:layout_marginEnd="12dp" />
<LinearLayout
@@ -49,7 +52,7 @@
android:clipToPadding="false"
android:orientation="horizontal"
android:layout_alignParentEnd="true"
- android:layout_marginEnd="10dp">
+ android:layout_marginEnd="12dp">
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/settings_button_container"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 086e9f4..25ff6b1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -173,7 +173,7 @@
<dimen name="borderless_button_radius">2dp</dimen>
<!-- How far the expanded QS panel peeks from the header in collapsed state. -->
- <dimen name="qs_peek_height">8dp</dimen>
+ <dimen name="qs_peek_height">0dp</dimen>
<!-- Zen mode panel: condition item button padding -->
<dimen name="zen_mode_condition_detail_button_padding">8dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/QSQuickTileView.java b/packages/SystemUI/src/com/android/systemui/QSQuickTileView.java
deleted file mode 100644
index 3362650..0000000
--- a/packages/SystemUI/src/com/android/systemui/QSQuickTileView.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2015 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;
-
-import android.content.Context;
-import android.view.View;
-import android.widget.ImageView;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileBaseView;
-
-public class QSQuickTileView extends QSTileBaseView {
-
- private final int mPadding;
- private final ImageView mIcon;
-
- public QSQuickTileView(Context context) {
- super(context);
- mPadding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
- mIcon = createIcon();
- addView(mIcon);
- }
-
- protected ImageView createIcon() {
- final ImageView icon = new ImageView(mContext);
- icon.setId(android.R.id.icon);
- icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
- return icon;
- }
-
- @Override
- public void init(OnClickListener click, OnClickListener clickSecondary,
- OnLongClickListener longClick) {
- setClickable(true);
- setOnClickListener(click);
- }
-
- @Override
- protected void handleStateChanged(QSTile.State state) {
- mIcon.setImageDrawable(state.icon.getDrawable(getContext()));
- setContentDescription(state.contentDescription);
- }
-
- @Override
- public boolean setType(int type) {
- return false;
- }
-
- @Override
- public View updateAccessibilityOrder(View previousView) {
- return this;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- mIcon.measure(exactly(getMeasuredWidth() - 2 * mPadding),
- exactly(getMeasuredHeight() - 2 * mPadding));
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- layout(mIcon, mPadding, mPadding);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 0e4a4e5..ca4a03a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -73,11 +73,6 @@
}
@Override
- public void setTileVisibility(TileRecord tile, int visibility) {
- tile.tileView.setVisibility(visibility);
- }
-
- @Override
public void addTile(TileRecord tile) {
mTiles.add(tile);
distributeTiles();
@@ -101,10 +96,6 @@
final int NT = mTiles.size();
for (int i = 0; i < NT; i++) {
TileRecord tile = mTiles.get(i);
- if (tile.tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
- // Don't show any quick tiles for now.
- continue;
- }
if (mPages.get(index).isFull()) {
if (++index == mPages.size()) {
if (DEBUG) Log.d(TAG, "Adding page for " + tile.tile.getClass().getSimpleName());
@@ -178,7 +169,6 @@
public TilePage(Context context, AttributeSet attrs) {
super(context, attrs);
- mAllowDual = false;
updateResources();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
new file mode 100644
index 0000000..b56ad76
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015 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.qs;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import com.android.systemui.R;
+
+import java.util.Objects;
+
+public class QSIconView extends ViewGroup {
+
+ private final View mIcon;
+ private final int mIconSizePx;
+ private final int mTilePaddingBelowIconPx;
+
+ public QSIconView(Context context) {
+ super(context);
+
+ final Resources res = context.getResources();
+ mIconSizePx = res.getDimensionPixelSize(R.dimen.qs_tile_icon_size);
+ mTilePaddingBelowIconPx = res.getDimensionPixelSize(R.dimen.qs_tile_padding_below_icon);
+
+ mIcon = createIcon();
+ addView(mIcon);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int w = MeasureSpec.getSize(widthMeasureSpec);
+ final int iconSpec = exactly(mIconSizePx);
+ mIcon.measure(MeasureSpec.makeMeasureSpec(w, getIconMeasureMode()), iconSpec);
+ setMeasuredDimension(w, mIcon.getMeasuredHeight() + mTilePaddingBelowIconPx);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int w = getMeasuredWidth();
+ final int h = getMeasuredHeight();
+ int top = 0;
+ final int iconLeft = (w - mIcon.getMeasuredWidth()) / 2;
+ layout(mIcon, iconLeft, top);
+ }
+
+ public void setIcon(QSTile.State state) {
+ setIcon((ImageView) mIcon, state);
+ }
+
+ protected void setIcon(ImageView iv, QSTile.State state) {
+ if (!Objects.equals(state.icon, iv.getTag(R.id.qs_icon_tag))) {
+ Drawable d = state.icon != null ? state.icon.getDrawable(mContext) : null;
+ if (d != null && state.autoMirrorDrawable) {
+ d.setAutoMirrored(true);
+ }
+ iv.setImageDrawable(d);
+ iv.setTag(R.id.qs_icon_tag, state.icon);
+ if (d instanceof Animatable) {
+ Animatable a = (Animatable) d;
+ if (state.icon instanceof QSTile.AnimationIcon && !iv.isShown()) {
+ a.stop(); // skip directly to end state
+ }
+ }
+ }
+
+ }
+
+ protected int getIconMeasureMode() {
+ return MeasureSpec.EXACTLY;
+ }
+
+ protected View createIcon() {
+ final ImageView icon = new ImageView(mContext);
+ icon.setId(android.R.id.icon);
+ icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+ return icon;
+ }
+
+ protected static int exactly(int size) {
+ return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
+ }
+
+ protected static void layout(View child, int left, int top) {
+ child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index bb2b8fc..5d3fbe2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -298,19 +298,10 @@
showDetail(show, r);
}
- private void showDetail(boolean show, Record r) {
+ protected void showDetail(boolean show, Record r) {
mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget();
}
- private void setTileVisibility(TileRecord record, int visibility) {
- mHandler.obtainMessage(H.SET_TILE_VISIBILITY, visibility, 0, record).sendToTarget();
- }
-
- private void handleSetTileVisibility(TileRecord tile, int visibility) {
- if (visibility == tile.tileView.getVisibility()) return;
- mTileLayout.setTileVisibility(tile, visibility);
- }
-
public void setTiles(Collection<QSTile<?>> tiles) {
for (TileRecord record : mRecords) {
mTileLayout.removeTile(record);
@@ -325,16 +316,17 @@
}
private void drawTile(TileRecord r, QSTile.State state) {
- final int visibility = state.visible ? VISIBLE : GONE;
- setTileVisibility(r, visibility);
r.tileView.onStateChanged(state);
}
+ protected QSTileBaseView createTileView(QSTile<?> tile) {
+ return new QSTileView(mContext, tile.createTileView(mContext));
+ }
+
protected void addTile(final QSTile<?> tile) {
final TileRecord r = new TileRecord();
r.tile = tile;
- r.tileView = tile.createTileView(mContext);
- r.tileView.setVisibility(View.GONE);
+ r.tileView = createTileView(tile);
final QSTile.Callback callback = new QSTile.Callback() {
@Override
public void onStateChanged(QSTile.State state) {
@@ -369,13 +361,7 @@
final View.OnClickListener click = new View.OnClickListener() {
@Override
public void onClick(View v) {
- r.tile.click();
- }
- };
- final View.OnClickListener clickSecondary = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- r.tile.secondaryClick();
+ onTileClick(r.tile);
}
};
final View.OnLongClickListener longClick = new View.OnLongClickListener() {
@@ -396,7 +382,7 @@
return true;
}
};
- r.tileView.init(click, clickSecondary, longClick);
+ r.tileView.init(click, longClick);
r.tile.setListening(mListening);
callback.onStateChanged(r.tile.getState());
r.tile.refreshState();
@@ -407,6 +393,10 @@
}
}
+ protected void onTileClick(QSTile<?> tile) {
+ tile.click();
+ }
+
public boolean isShowingDetail() {
return mDetailRecord != null
|| (mCustomizePanel != null && mCustomizePanel.isCustomizing());
@@ -429,7 +419,7 @@
return mQsContainer.getMeasuredHeight();
}
- private void handleShowDetail(Record r, boolean show) {
+ protected void handleShowDetail(Record r, boolean show) {
if (r instanceof TileRecord) {
handleShowDetailTile((TileRecord) r, show);
} else {
@@ -515,9 +505,7 @@
private void logTiles() {
for (int i = 0; i < mRecords.size(); i++) {
TileRecord tileRecord = mRecords.get(i);
- if (tileRecord.tile.getState().visible) {
- MetricsLogger.visible(mContext, tileRecord.tile.getMetricsCategory());
- }
+ MetricsLogger.visible(mContext, tileRecord.tile.getMetricsCategory());
}
}
@@ -554,13 +542,11 @@
public void handleMessage(Message msg) {
if (msg.what == SHOW_DETAIL) {
handleShowDetail((Record)msg.obj, msg.arg1 != 0);
- } else if (msg.what == SET_TILE_VISIBILITY) {
- handleSetTileVisibility((TileRecord) msg.obj, msg.arg1);
}
}
}
- private static class Record {
+ protected static class Record {
View detailView;
DetailAdapter detailAdapter;
int x;
@@ -619,7 +605,6 @@
public interface QSTileLayout {
void addTile(TileRecord tile);
void removeTile(TileRecord tile);
- void setTileVisibility(TileRecord tile, int visibility);
int getOffsetTop(TileRecord tile);
void updateResources();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 7f45545..39f0c55 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -86,20 +86,12 @@
mTileSpec = tileSpec;
}
- public int getTileType() {
- return QSTileView.QS_TYPE_NORMAL;
- }
-
- public final boolean supportsDualTargets() {
- return getTileType() == QSTileView.QS_TYPE_DUAL;
- }
-
public Host getHost() {
return mHost;
}
- public QSTileBaseView createTileView(Context context) {
- return new QSTileView(context);
+ public QSIconView createTileView(Context context) {
+ return new QSIconView(context);
}
public DetailAdapter getDetailAdapter() {
@@ -181,7 +173,8 @@
}
protected void handleSecondaryClick() {
- // optional
+ // Default to normal click.
+ handleClick();
}
protected void handleLongClick() {
@@ -327,8 +320,10 @@
public interface Host {
void startActivityDismissingKeyguard(Intent intent);
void startActivityDismissingKeyguard(PendingIntent intent);
+ void startRunnableDismissingKeyguard(Runnable runnable);
void warn(String message, Throwable t);
void collapsePanels();
+ void openPanels();
Looper getLooper();
Context getContext();
Collection<QSTile<?>> getTiles();
@@ -345,6 +340,7 @@
UserSwitcherController getUserSwitcherController();
UserInfoController getUserInfoController();
BatteryController getBatteryController();
+ void removeTile(String tileSpec);
public interface Callback {
void onTilesChanged();
@@ -451,7 +447,6 @@
}
public static class State {
- public boolean visible;
public Icon icon;
public CharSequence label;
public CharSequence contentDescription;
@@ -461,14 +456,12 @@
public boolean copyTo(State other) {
if (other == null) throw new IllegalArgumentException();
if (!other.getClass().equals(getClass())) throw new IllegalArgumentException();
- final boolean changed = other.visible != visible
- || !Objects.equals(other.icon, icon)
+ final boolean changed = !Objects.equals(other.icon, icon)
|| !Objects.equals(other.label, label)
|| !Objects.equals(other.contentDescription, contentDescription)
|| !Objects.equals(other.autoMirrorDrawable, autoMirrorDrawable)
|| !Objects.equals(other.dualLabelContentDescription,
dualLabelContentDescription);
- other.visible = visible;
other.icon = icon;
other.label = label;
other.contentDescription = contentDescription;
@@ -484,7 +477,6 @@
protected StringBuilder toStringBuilder() {
final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
- sb.append("visible=").append(visible);
sb.append(",icon=").append(icon);
sb.append(",label=").append(label);
sb.append(",contentDescription=").append(contentDescription);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
index 72fc88d..68461f5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
@@ -1,57 +1,112 @@
/*
* Copyright (C) 2015 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
+ * 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.
+ * 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.qs;
import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.util.AttributeSet;
import android.view.View;
-import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import com.android.systemui.R;
-public abstract class QSTileBaseView extends ViewGroup {
-
- public static final int QS_TYPE_NORMAL = 0;
- public static final int QS_TYPE_DUAL = 1;
- public static final int QS_TYPE_QUICK = 2;
+public class QSTileBaseView extends LinearLayout {
private final H mHandler = new H();
+ private QSIconView mIcon;
+ private RippleDrawable mRipple;
+ private Drawable mTileBackground;
- public QSTileBaseView(Context context) {
+ public QSTileBaseView(Context context, QSIconView icon) {
super(context);
+ mIcon = icon;
+ addView(mIcon);
+
+ mTileBackground = newTileBackground();
+ if (mTileBackground instanceof RippleDrawable) {
+ setRipple((RippleDrawable) mTileBackground);
+ }
+ setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ setBackground(mTileBackground);
+
+ // Default to Quick Tile padding, and QSTileView will specify its own padding.
+ int padding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
+ setPadding(padding, padding, padding, padding);
}
- public QSTileBaseView(Context context, AttributeSet attrs) {
- super(context, attrs);
+ private Drawable newTileBackground() {
+ final int[] attrs = new int[] { android.R.attr.selectableItemBackgroundBorderless };
+ final TypedArray ta = mContext.obtainStyledAttributes(attrs);
+ final Drawable d = ta.getDrawable(0);
+ ta.recycle();
+ return d;
}
+ private void setRipple(RippleDrawable tileBackground) {
+ mRipple = tileBackground;
+ if (getWidth() != 0) {
+ updateRippleSize(getWidth(), getHeight());
+ }
+ }
+
+ private void updateRippleSize(int width, int height) {
+ // center the touch feedback on the center of the icon, and dial it down a bit
+ final int cx = width / 2;
+ final int cy = height / 2;
+ final int rad = (int)(mIcon.getHeight() * .85f);
+ mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
+ }
+
+ public void init(OnClickListener click, OnLongClickListener longClick) {
+ setClickable(true);
+ setOnClickListener(click);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ final int w = getMeasuredWidth();
+ final int h = getMeasuredHeight();
+
+ if (mRipple != null) {
+ updateRippleSize(w, h);
+ }
+ }
+
+ /**
+ * Update the accessibility order for this view.
+ *
+ * @param previousView the view which should be before this one
+ * @return the last view in this view which is accessible
+ */
+ public View updateAccessibilityOrder(View previousView) {
+ setAccessibilityTraversalAfter(previousView.getId());
+ return this;
+ }
public void onStateChanged(QSTile.State state) {
mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget();
}
- public abstract void init(OnClickListener click, OnClickListener clickSecondary,
- OnLongClickListener longClick);
- public abstract View updateAccessibilityOrder(View previousView);
- public abstract boolean setType(int type);
-
- protected abstract void handleStateChanged(QSTile.State state);
-
- protected static int exactly(int size) {
- return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
- }
-
- protected static void layout(View child, int left, int top) {
- child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
+ protected void handleStateChanged(QSTile.State state) {
+ mIcon.setIcon(state);
+ setContentDescription(state.contentDescription);
}
private class H extends Handler {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index 4cc2b8d..41ac4d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -17,26 +17,16 @@
package com.android.systemui.qs;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.graphics.Typeface;
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.RippleDrawable;
import android.util.MathUtils;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
import android.widget.TextView;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
-import com.android.systemui.qs.QSTile.AnimationIcon;
-
-import java.util.Objects;
/** View that represents a standard quick settings tile. **/
public class QSTileView extends QSTileBaseView {
@@ -44,59 +34,25 @@
Typeface.NORMAL);
protected final Context mContext;
- private final View mIcon;
- private final View mDivider;
- private final int mIconSizePx;
private final int mTileSpacingPx;
private int mTilePaddingTopPx;
- private final int mTilePaddingBelowIconPx;
- private final int mDualTileVerticalPaddingPx;
- private final View mTopBackgroundView;
private TextView mLabel;
- private QSDualTileLabel mDualLabel;
- private int mType;
- private OnClickListener mClickPrimary;
- private OnClickListener mClickSecondary;
- private OnLongClickListener mLongClick;
- private Drawable mTileBackground;
- private RippleDrawable mRipple;
- private View mCircle;
-
- public QSTileView(Context context) {
- super(context);
+ public QSTileView(Context context, QSIconView icon) {
+ super(context, icon);
mContext = context;
final Resources res = context.getResources();
- mIconSizePx = res.getDimensionPixelSize(R.dimen.qs_tile_icon_size);
mTileSpacingPx = res.getDimensionPixelSize(R.dimen.qs_tile_spacing);
- mTilePaddingBelowIconPx = res.getDimensionPixelSize(R.dimen.qs_tile_padding_below_icon);
- mDualTileVerticalPaddingPx =
- res.getDimensionPixelSize(R.dimen.qs_dual_tile_padding_vertical);
- mTileBackground = newTileBackground();
- recreateLabel();
setClipChildren(false);
- mTopBackgroundView = new View(context);
- mTopBackgroundView.setId(View.generateViewId());
- addView(mTopBackgroundView);
-
- mIcon = createIcon();
- addView(mIcon);
-
- mCircle = createCircleIcon();
- addView(mCircle);
-
- mDivider = new View(mContext);
- mDivider.setBackgroundColor(context.getColor(R.color.qs_tile_divider));
- final int dh = res.getDimensionPixelSize(R.dimen.qs_tile_divider_height);
- mDivider.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, dh));
- addView(mDivider);
-
setClickable(true);
updateTopPadding();
setId(View.generateViewId());
+ createLabel();
+ setOrientation(VERTICAL);
+ setGravity(Gravity.CENTER);
}
private void updateTopPadding() {
@@ -106,6 +62,8 @@
float largeFactor = (MathUtils.constrain(getResources().getConfiguration().fontScale,
1.0f, FontSizeUtils.LARGE_TEXT_SCALE) - 1f) / (FontSizeUtils.LARGE_TEXT_SCALE - 1f);
mTilePaddingTopPx = Math.round((1 - largeFactor) * padding + largeFactor * largePadding);
+ setPadding(mTileSpacingPx, mTilePaddingTopPx + mTileSpacingPx, mTileSpacingPx,
+ mTileSpacingPx);
requestLayout();
}
@@ -114,260 +72,29 @@
super.onConfigurationChanged(newConfig);
updateTopPadding();
FontSizeUtils.updateFontSize(mLabel, R.dimen.qs_tile_text_size);
- if (mDualLabel != null) {
- mDualLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- getResources().getDimensionPixelSize(R.dimen.qs_tile_text_size));
- }
}
- private void recreateLabel() {
- CharSequence labelText = null;
- CharSequence labelDescription = null;
- if (mLabel != null) {
- labelText = mLabel.getText();
- removeView(mLabel);
- mLabel = null;
- }
- if (mDualLabel != null) {
- labelText = mDualLabel.getText();
- labelDescription = mLabel != null ? mLabel.getContentDescription() : null;
- removeView(mDualLabel);
- mDualLabel = null;
- }
+ private void createLabel() {
final Resources res = mContext.getResources();
- if (mType == QS_TYPE_DUAL) {
- mDualLabel = new QSDualTileLabel(mContext);
- mDualLabel.setId(View.generateViewId());
- mDualLabel.setBackgroundResource(R.drawable.btn_borderless_rect);
- mDualLabel.setFirstLineCaret(mContext.getDrawable(R.drawable.qs_dual_tile_caret));
- mDualLabel.setTextColor(mContext.getColor(R.color.qs_tile_text));
- mDualLabel.setPadding(0, mDualTileVerticalPaddingPx, 0, mDualTileVerticalPaddingPx);
- mDualLabel.setTypeface(CONDENSED);
- mDualLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- res.getDimensionPixelSize(R.dimen.qs_tile_text_size));
- mDualLabel.setClickable(true);
- mDualLabel.setOnClickListener(mClickSecondary);
- mDualLabel.setFocusable(true);
- if (labelText != null) {
- mDualLabel.setText(labelText);
- }
- if (labelDescription != null) {
- mDualLabel.setContentDescription(labelDescription);
- }
- addView(mDualLabel);
- mDualLabel.setAccessibilityTraversalAfter(mTopBackgroundView.getId());
- } else if (mType == QS_TYPE_NORMAL) {
- mLabel = new TextView(mContext);
- mLabel.setTextColor(mContext.getColor(R.color.qs_tile_text));
- mLabel.setGravity(Gravity.CENTER_HORIZONTAL);
- mLabel.setMinLines(2);
- mLabel.setPadding(0, 0, 0, 0);
- mLabel.setTypeface(CONDENSED);
- mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- res.getDimensionPixelSize(R.dimen.qs_tile_text_size));
- mLabel.setClickable(false);
- if (labelText != null) {
- mLabel.setText(labelText);
- }
- addView(mLabel);
- }
+ mLabel = new TextView(mContext);
+ mLabel.setTextColor(mContext.getColor(R.color.qs_tile_text));
+ mLabel.setGravity(Gravity.CENTER_HORIZONTAL);
+ mLabel.setMinLines(2);
+ mLabel.setPadding(0, 0, 0, 0);
+ mLabel.setTypeface(CONDENSED);
+ mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ res.getDimensionPixelSize(R.dimen.qs_tile_text_size));
+ mLabel.setClickable(false);
+ addView(mLabel);
}
- public boolean setType(int type) {
- final boolean changed = mType != type;
- mType = type;
- if (changed) {
- recreateLabel();
- }
- if (mTileBackground instanceof RippleDrawable) {
- setRipple((RippleDrawable) mTileBackground);
- }
- if (mType == QS_TYPE_DUAL) {
- mTopBackgroundView.setOnClickListener(mClickPrimary);
- setOnClickListener(null);
- setClickable(false);
- setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- mTopBackgroundView.setBackground(mTileBackground);
- } else {
- mTopBackgroundView.setOnClickListener(null);
- mTopBackgroundView.setClickable(false);
- setOnClickListener(mClickPrimary);
- setOnLongClickListener(mLongClick);
- setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- setBackground(mTileBackground);
- }
- mTopBackgroundView.setFocusable(mType == QS_TYPE_DUAL);
- setFocusable(mType != QS_TYPE_DUAL);
- mDivider.setVisibility(mType == QS_TYPE_DUAL ? VISIBLE : GONE);
- mCircle.setVisibility(mType == QS_TYPE_QUICK ? VISIBLE : GONE);
- postInvalidate();
- return changed;
- }
-
- private void setRipple(RippleDrawable tileBackground) {
- mRipple = tileBackground;
- if (getWidth() != 0) {
- updateRippleSize(getWidth(), getHeight());
- }
- }
-
- public void init(OnClickListener clickPrimary, OnClickListener clickSecondary,
- OnLongClickListener longClick) {
- mClickPrimary = clickPrimary;
- mClickSecondary = clickSecondary;
- mLongClick = longClick;
- }
-
- protected View createIcon() {
- final ImageView icon = new ImageView(mContext);
- icon.setId(android.R.id.icon);
- icon.setScaleType(ScaleType.CENTER_INSIDE);
- return icon;
- }
-
- protected View createCircleIcon() {
- final ImageView icon = new ImageView(mContext);
- icon.setImageResource(R.drawable.ic_qs_circle);
- // TODO: Not this.
- icon.setPadding(20, 20, 20, 20);
- return icon;
- }
-
- protected View createCircle() {
- final ImageView icon = new ImageView(mContext);
- icon.setId(android.R.id.icon);
- icon.setScaleType(ScaleType.CENTER_INSIDE);
- return icon;
- }
-
- private Drawable newTileBackground() {
- final int[] attrs = new int[] { android.R.attr.selectableItemBackgroundBorderless };
- final TypedArray ta = mContext.obtainStyledAttributes(attrs);
- final Drawable d = ta.getDrawable(0);
- ta.recycle();
- return d;
- }
-
- private View labelView() {
- return mType == QS_TYPE_DUAL ? mDualLabel : mLabel;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int w = MeasureSpec.getSize(widthMeasureSpec);
- final int h = MeasureSpec.getSize(heightMeasureSpec);
- final int iconSpec = exactly(mIconSizePx);
- mIcon.measure(MeasureSpec.makeMeasureSpec(w, getIconMeasureMode()), iconSpec);
- switch (mType) {
- case QS_TYPE_QUICK:
- mCircle.measure(
- MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY));
- break;
- case QS_TYPE_DUAL:
- mDivider.measure(widthMeasureSpec, exactly(mDivider.getLayoutParams().height));
- default:
- labelView().measure(widthMeasureSpec,
- MeasureSpec.makeMeasureSpec(h, MeasureSpec.AT_MOST));
- break;
- }
- int heightSpec = exactly(
- mIconSizePx + mTilePaddingBelowIconPx + mTilePaddingTopPx);
- mTopBackgroundView.measure(widthMeasureSpec, heightSpec);
- setMeasuredDimension(w, h);
- }
-
- protected int getIconMeasureMode() {
- return MeasureSpec.EXACTLY;
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- final int w = getMeasuredWidth();
- final int h = getMeasuredHeight();
-
- layout(mTopBackgroundView, 0, mTileSpacingPx);
-
- int top = 0;
- top += mTileSpacingPx;
- top += mTilePaddingTopPx;
- final int iconLeft = (w - mIcon.getMeasuredWidth()) / 2;
- if (mType == QS_TYPE_QUICK) {
- top = (h - mIcon.getMeasuredHeight()) / 2;
- layout(mCircle, 0, 0);
- }
- layout(mIcon, iconLeft, top);
- if (mRipple != null) {
- updateRippleSize(w, h);
-
- }
- top = mIcon.getBottom();
- top += mTilePaddingBelowIconPx;
- if (mType == QS_TYPE_DUAL) {
- layout(mDivider, 0, top);
- top = mDivider.getBottom();
- }
- if (mType != QS_TYPE_QUICK) {
- layout(labelView(), 0, top);
- }
- }
-
- private void updateRippleSize(int width, int height) {
- // center the touch feedback on the center of the icon, and dial it down a bit
- final int cx = width / 2;
- final int cy = mType == QS_TYPE_DUAL ? mIcon.getTop() + mIcon.getHeight() / 2 : height / 2;
- final int rad = (int)(mIcon.getHeight() * 1.25f);
- mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
+ public void init(OnClickListener clickPrimary, OnLongClickListener longClick) {
+ setOnClickListener(clickPrimary);
+ setOnLongClickListener(longClick);
}
protected void handleStateChanged(QSTile.State state) {
- if (mIcon instanceof ImageView) {
- setIcon((ImageView) mIcon, state);
- }
- if (mType == QS_TYPE_DUAL) {
- mDualLabel.setText(state.label);
- mDualLabel.setContentDescription(state.dualLabelContentDescription);
- mTopBackgroundView.setContentDescription(state.contentDescription);
- } else if (mType == QS_TYPE_NORMAL) {
- mLabel.setText(state.label);
- setContentDescription(state.contentDescription);
- }
- }
-
- protected void setIcon(ImageView iv, QSTile.State state) {
- if (!Objects.equals(state.icon, iv.getTag(R.id.qs_icon_tag))) {
- Drawable d = state.icon != null ? state.icon.getDrawable(mContext) : null;
- if (d != null && state.autoMirrorDrawable) {
- d.setAutoMirrored(true);
- }
- iv.setImageDrawable(d);
- iv.setTag(R.id.qs_icon_tag, state.icon);
- if (d instanceof Animatable) {
- Animatable a = (Animatable) d;
- if (state.icon instanceof AnimationIcon && !iv.isShown()) {
- a.stop(); // skip directly to end state
- }
- }
- }
- }
-
- /**
- * Update the accessibility order for this view.
- *
- * @param previousView the view which should be before this one
- * @return the last view in this view which is accessible
- */
- public View updateAccessibilityOrder(View previousView) {
- View firstView;
- View lastView;
- if (mType == QS_TYPE_DUAL) {
- lastView = mDualLabel;
- firstView = mTopBackgroundView;
- } else {
- firstView = this;
- lastView = this;
- }
- firstView.setAccessibilityTraversalAfter(previousView.getId());
- return lastView;
+ super.handleStateChanged(state);
+ mLabel.setText(state.label);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 6a053be..fe8ce9b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.ImageView;
@@ -30,10 +29,14 @@
import java.util.Collection;
/**
- * Version of QSPanel that only shows 4 Quick Tiles in the QS Header.
+ * Version of QSPanel that only shows N Quick Tiles in the QS Header.
*/
public class QuickQSPanel extends QSPanel {
+ private int mMaxTiles;
+ private QSPanel mFullPanel;
+ private View mHeader;
+
public QuickQSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
if (mTileLayout != null) {
@@ -46,6 +49,36 @@
mQsContainer.addView((View) mTileLayout, 1 /* Between brightness and footer */);
}
+ public void setQSPanelAndHeader(QSPanel fullPanel, View header) {
+ mFullPanel = fullPanel;
+ mHeader = header;
+ }
+
+ @Override
+ protected void handleShowDetail(QSPanel.Record r, boolean show) {
+ if (show) {
+ mHeader.performClick();
+ mFullPanel.showDetail(show, r);
+ } else {
+ // Not sure how we would end up here...
+ super.handleShowDetail(r, show);
+ }
+ }
+
+ @Override
+ protected QSTileBaseView createTileView(QSTile<?> tile) {
+ return new QSTileBaseView(mContext, tile.createTileView(mContext));
+ }
+
+ public void setMaxTiles(int maxTiles) {
+ mMaxTiles = maxTiles;
+ }
+
+ @Override
+ protected void onTileClick(QSTile<?> tile) {
+ tile.secondaryClick();
+ }
+
@Override
public void onTuningChanged(String key, String newValue) {
// No tunings for you.
@@ -59,11 +92,8 @@
public void setTiles(Collection<QSTile<?>> tiles) {
ArrayList<QSTile<?>> quickTiles = new ArrayList<>();
for (QSTile<?> tile : tiles) {
- if (tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
- Log.d("QSPanel", "Adding " + tile.getTileSpec());
- quickTiles.add(tile);
- }
- if (quickTiles.size() == 2) {
+ quickTiles.add(tile);
+ if (quickTiles.size() == mMaxTiles) {
break;
}
}
@@ -74,6 +104,8 @@
public HeaderTileLayout(Context context) {
super(context);
+ setClipChildren(false);
+ setClipToPadding(false);
setGravity(Gravity.CENTER_VERTICAL);
setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
@@ -92,17 +124,13 @@
@Override
public void addTile(TileRecord tile) {
tile.tileView.setLayoutParams(generateLayoutParams());
- // These shouldn't be normal tiles, but they will be for now so that the circles don't
- // show up.
- tile.tileView.setType(QSTileView.QS_TYPE_NORMAL);
addView(tile.tileView, getChildCount() - 1 /* Leave icon at end */);
}
private LayoutParams generateLayoutParams() {
int size =
mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
- LayoutParams lp = new LayoutParams(0, size);
- lp.weight = 1;
+ LayoutParams lp = new LayoutParams(size, size);
return lp;
}
@@ -112,11 +140,6 @@
}
@Override
- public void setTileVisibility(TileRecord tile, int visibility) {
- tile.tileView.setVisibility(visibility);
- }
-
- @Override
public int getOffsetTop(TileRecord tile) {
return 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
index a2e1296..b2bfa06 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
@@ -27,7 +27,7 @@
import com.android.systemui.qs.QSTile.SignalState;
/** View that represents a custom quick settings tile for displaying signal info (wifi/cell). **/
-public final class SignalTileView extends QSTileView {
+public final class SignalTileView extends QSIconView {
private static final long DEFAULT_DURATION = new ValueAnimator().getDuration();
private static final long SHORT_DURATION = DEFAULT_DURATION / 3;
@@ -106,8 +106,7 @@
}
@Override
- protected void handleStateChanged(QSTile.State state) {
- super.handleStateChanged(state);
+ public void setIcon(QSTile.State state) {
final SignalState s = (SignalState) state;
setIcon(mSignal, s);
if (s.overlayIconId > 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 1336eec..ff11177 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -18,14 +18,9 @@
private static final String TAG = "TileLayout";
- private int mDualTileUnderlap;
protected int mColumns;
private int mCellWidth;
private int mCellHeight;
- private int mLargeCellWidth;
- private int mLargeCellHeight;
-
- protected boolean mAllowDual = true;
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
@@ -59,21 +54,11 @@
super.removeAllViews();
}
- @Override
- public void setTileVisibility(TileRecord tile, int visibility) {
- tile.tileView.setVisibility(visibility);
- }
-
public void updateResources() {
final Resources res = mContext.getResources();
final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
mCellHeight = getCellHeight();
mCellWidth = (int) (mCellHeight * TILE_ASPECT);
- mLargeCellHeight = mAllowDual ? res.getDimensionPixelSize(R.dimen.qs_dual_tile_height)
- : mCellHeight;
- mLargeCellWidth = mAllowDual ? (int) (mLargeCellHeight * TILE_ASPECT) : mCellWidth;
- mDualTileUnderlap = mAllowDual
- ? res.getDimensionPixelSize(R.dimen.qs_dual_tile_padding_vertical) : 0;
if (mColumns != columns) {
mColumns = columns;
postInvalidate();
@@ -90,16 +75,13 @@
int r = -1;
int c = -1;
int rows = 0;
- boolean rowIsDual = false;
for (TileRecord record : mRecords) {
if (record.tileView.getVisibility() == GONE) continue;
// wrap to next column if we've reached the max # of columns
// also don't allow dual + single tiles on the same row
- if (r == -1 || c == (mColumns - 1)
- || rowIsDual != (mAllowDual && record.tile.supportsDualTargets())) {
+ if (r == -1 || c == (mColumns - 1)) {
r++;
c = 0;
- rowIsDual = mAllowDual && record.tile.supportsDualTargets();
} else {
c++;
}
@@ -110,13 +92,9 @@
View previousView = this;
for (TileRecord record : mRecords) {
- if (record.tileView.setType(mAllowDual ? record.tile.getTileType()
- : QSTileView.QS_TYPE_NORMAL)) {
- record.tileView.handleStateChanged(record.tile.getState());
- }
if (record.tileView.getVisibility() == GONE) continue;
- final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth;
- final int ch = record.row == 0 ? mLargeCellHeight : mCellHeight;
+ final int cw = mCellWidth;
+ final int ch = mCellHeight;
record.tileView.measure(exactly(cw), exactly(ch));
previousView = record.tileView.updateAccessibilityOrder(previousView);
}
@@ -135,7 +113,7 @@
for (TileRecord record : mRecords) {
if (record.tileView.getVisibility() == GONE) continue;
final int cols = getColumnCount(record.row);
- final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth;
+ final int cw = mCellWidth;
final int extra = (w - cw * cols) / (cols + 1);
int left = record.col * cw + (record.col + 1) * extra;
final int top = getRowTop(record.row);
@@ -153,7 +131,7 @@
private int getRowTop(int row) {
if (row <= 0) return 0;
- return mLargeCellHeight - mDualTileUnderlap + (row - 1) * mCellHeight;
+ return row * mCellHeight;
}
private int getColumnCount(int row) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java b/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java
deleted file mode 100644
index e64f6a0..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2014 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.qs;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-
-import com.android.systemui.Prefs;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.statusbar.policy.Listenable;
-
-public class UsageTracker implements Listenable {
- private static final long MILLIS_PER_DAY = 1000 * 60 * 60 * 24;
-
- private final Context mContext;
- private final long mTimeToShowTile;
- @Prefs.Key private final String mPrefKey;
- private final String mResetAction;
-
- private boolean mRegistered;
-
- public UsageTracker(Context context, @Prefs.Key String prefKey, Class<?> tile,
- int timeoutResource) {
- mContext = context;
- mPrefKey = prefKey;
- mTimeToShowTile = MILLIS_PER_DAY * mContext.getResources().getInteger(timeoutResource);
- mResetAction = "com.android.systemui.qs." + tile.getSimpleName() + ".usage_reset";
- }
-
- @Override
- public void setListening(boolean listen) {
- if (listen && !mRegistered) {
- mContext.registerReceiver(mReceiver, new IntentFilter(mResetAction));
- mRegistered = true;
- } else if (!listen && mRegistered) {
- mContext.unregisterReceiver(mReceiver);
- mRegistered = false;
- }
- }
-
- public boolean isRecentlyUsed() {
- long lastUsed = Prefs.getLong(mContext, mPrefKey, 0L /* defaultValue */);
- return (System.currentTimeMillis() - lastUsed) < mTimeToShowTile;
- }
-
- public void trackUsage() {
- Prefs.putLong(mContext, mPrefKey, System.currentTimeMillis());
- }
-
- public void reset() {
- Prefs.remove(mContext, mPrefKey);
- }
-
- public void showResetConfirmation(String title, final Runnable onConfirmed) {
- final SystemUIDialog d = new SystemUIDialog(mContext);
- d.setTitle(title);
- d.setMessage(mContext.getString(R.string.quick_settings_reset_confirmation_message));
- d.setNegativeButton(android.R.string.cancel, null);
- d.setPositiveButton(R.string.quick_settings_reset_confirmation_button,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- reset();
- if (onConfirmed != null) {
- onConfirmed.run();
- }
- }
- });
- d.setCanceledOnTouchOutside(true);
- d.show();
- }
-
- private BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mResetAction.equals(intent.getAction())) {
- reset();
- }
- }
- };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java
index a4ff685..95ff611 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java
@@ -72,12 +72,10 @@
try {
PackageManager pm = mContext.getPackageManager();
ServiceInfo info = pm.getServiceInfo(mComponent, 0);
- state.visible = true;
state.icon = new DrawableIcon(info.loadIcon(pm));
state.label = info.loadLabel(pm).toString();
state.contentDescription = state.label;
} catch (Exception e) {
- state.visible = false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
index 422ae4d..8dda9ba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
@@ -176,13 +176,16 @@
if (mTiles.get(i).startsWith(CustomTile.PREFIX)) {
mCurrentTiles.add(BlankCustomTile.create(mHost, mTiles.get(i)));
} else {
- mCurrentTiles.add(mHost.createTile(mTiles.get(i)));
+ QSTile<?> tile = mHost.createTile(mTiles.get(i));
+ if (tile != null) {
+ mCurrentTiles.add(tile);
+ }
}
mCurrentTiles.get(mCurrentTiles.size() - 1).setTileSpec(mTiles.get(i));
}
super.setTiles(mCurrentTiles);
}
-
+
public void addTile(String spec) {
mTiles.add(spec);
setTilesInternal();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
index d0d5b54..3acbed8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
@@ -25,14 +25,12 @@
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.LinearLayout;
-
import com.android.systemui.R;
import com.android.systemui.qs.PagedTileLayout;
import com.android.systemui.qs.PagedTileLayout.TilePage;
import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.QSPanel.TileRecord;
import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileView;
import com.android.systemui.qs.QuickTileLayout;
import java.util.ArrayList;
@@ -75,13 +73,12 @@
public void addTile(TileRecord record) {
mTiles.add(record);
distributeTiles();
- if (record.tile.getTileType() == QSTileView.QS_TYPE_QUICK
- || record.tileView.getTag() == record.tile) {
+ if (record.tileView.getTag() == record.tile) {
return;
}
record.tileView.setTag(record.tile);
record.tileView.setVisibility(View.VISIBLE);
- record.tileView.init(null, null, null);
+ record.tileView.init(null, null);
record.tileView.setOnTouchListener(this);
if (mCurrentClip != null && mCurrentClip.getItemAt(0)
.getText().toString().equals(record.tile.getTileSpec())) {
@@ -107,10 +104,6 @@
final int NT = mTiles.size();
for (int i = 0; i < NT; i++) {
TileRecord tile = mTiles.get(i);
- if (tile.tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
- // Ignore quick tiles for now.
- continue;
- }
mPages.get(index).addTile(tile);
// Keep everything in one layout for now.
if (false && mPages.get(index).isFull()) {
@@ -125,12 +118,6 @@
}
@Override
- public void setTileVisibility(TileRecord tile, int visibility) {
- // All tiles visible here, so that they can be re-arranged.
- tile.tileView.setVisibility(View.VISIBLE);
- }
-
- @Override
public int getOffsetTop(TileRecord tile) {
// No touch feedback, so this isn't required.
return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 144b202..6706c7a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -88,6 +88,9 @@
}
Log.d(TAG, "Trying " + spec);
final QSTile<?> tile = host.createTile(spec);
+ if (tile == null) {
+ continue;
+ }
// Bad, bad, very bad.
tile.setListening(true);
tile.clearState();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 49f8d1c..fc802dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -73,7 +73,6 @@
final int value = arg instanceof Integer ? (Integer)arg : mSetting.getValue();
final boolean airplaneMode = value != 0;
state.value = airplaneMode;
- state.visible = true;
state.label = mContext.getString(R.string.airplane_mode);
if (airplaneMode) {
state.icon = mEnable;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index 8f9655d..84eac65 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -73,7 +73,6 @@
int level = (arg != null) ? (Integer) arg : mLevel;
String percentage = NumberFormat.getPercentInstance().format((double) level / 100.0);
- state.visible = true;
state.icon = new Icon() {
@Override
public Drawable getDrawable(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 7f07ddc..cfc09a0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -24,18 +24,15 @@
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
-
import com.android.internal.logging.MetricsLogger;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.systemui.R;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileView;
import com.android.systemui.statusbar.policy.BluetoothController;
import java.util.Collection;
-import java.util.Set;
/** Quick settings tile: Bluetooth **/
public class BluetoothTile extends QSTile<QSTile.BooleanState> {
@@ -44,21 +41,13 @@
private final BluetoothController mController;
private final BluetoothDetailAdapter mDetailAdapter;
- private final boolean mAlwaysDetail;
-
- public BluetoothTile(Host host, boolean alwaysDetail) {
+ public BluetoothTile(Host host) {
super(host);
- mAlwaysDetail = alwaysDetail;
mController = host.getBluetoothController();
mDetailAdapter = new BluetoothDetailAdapter();
}
@Override
- public int getTileType() {
- return QSTileView.QS_TYPE_DUAL;
- }
-
- @Override
public DetailAdapter getDetailAdapter() {
return mDetailAdapter;
}
@@ -78,18 +67,15 @@
}
@Override
- protected void handleClick() {
- if (mAlwaysDetail) {
- handleSecondaryClick();
- return;
- }
+ protected void handleSecondaryClick() {
+ // Secondary clicks are header clicks, just toggle.
final boolean isEnabled = (Boolean)mState.value;
MetricsLogger.action(mContext, getMetricsCategory(), !isEnabled);
mController.setBluetoothEnabled(!isEnabled);
}
@Override
- protected void handleSecondaryClick() {
+ protected void handleClick() {
if (!mState.value) {
mState.value = true;
mController.setBluetoothEnabled(true);
@@ -99,11 +85,9 @@
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- final boolean supported = mController.isBluetoothSupported();
final boolean enabled = mController.isBluetoothEnabled();
final boolean connected = mController.isBluetoothConnected();
final boolean connecting = mController.isBluetoothConnecting();
- state.visible = supported;
state.value = enabled;
state.autoMirrorDrawable = false;
if (enabled) {
@@ -155,6 +139,10 @@
}
}
+ public static boolean isSupported(Host host) {
+ return host.getBluetoothController().isBluetoothSupported();
+ }
+
private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
@Override
public void onBluetoothStateChange(boolean enabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 48b74a4..a8e139c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -23,12 +23,10 @@
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;
-
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
-import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
@@ -87,14 +85,23 @@
@Override
protected void handleClick() {
+ if (mKeyguard.isSecure() && !mKeyguard.canSkipBouncer()) {
+ mHost.startRunnableDismissingKeyguard(new Runnable() {
+ @Override
+ public void run() {
+ MetricsLogger.action(mContext, getMetricsCategory());
+ showDetail(true);
+ mHost.openPanels();
+ }
+ });
+ return;
+ }
MetricsLogger.action(mContext, getMetricsCategory());
showDetail(true);
}
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- state.visible = !mKeyguard.isSecure() || !mKeyguard.isShowing()
- || mKeyguard.canSkipBouncer() || QSPanel.isTheNewQS(mContext);
state.label = mContext.getString(R.string.quick_settings_cast_title);
state.value = false;
state.autoMirrorDrawable = false;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index a0bbbe3..5fb76c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -26,8 +26,8 @@
import com.android.internal.logging.MetricsLogger;
import com.android.settingslib.net.MobileDataController;
import com.android.systemui.R;
+import com.android.systemui.qs.QSIconView;
import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileBaseView;
import com.android.systemui.qs.SignalTileView;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
@@ -71,7 +71,7 @@
}
@Override
- public QSTileBaseView createTileView(Context context) {
+ public QSIconView createTileView(Context context) {
return new SignalTileView(context);
}
@@ -87,8 +87,6 @@
@Override
protected void handleUpdateState(SignalState state, Object arg) {
- state.visible = mController.hasMobileDataFeature();
- if (!state.visible) return;
CallbackInfo cb = (CallbackInfo) arg;
if (cb == null) {
cb = mSignalCallback.mInfo;
@@ -138,6 +136,10 @@
return string;
}
+ public static boolean isSupported(Host host) {
+ return host.getNetworkController().hasMobileDataFeature();
+ }
+
private static final class CallbackInfo {
boolean enabled;
boolean wifiEnabled;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 2f9a496..f73ee35 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -17,14 +17,10 @@
package com.android.systemui.qs.tiles;
import android.provider.Settings.Secure;
-
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.SecureSetting;
-import com.android.systemui.qs.UsageTracker;
/** Quick settings tile: Invert colors **/
public class ColorInversionTile extends QSTile<QSTile.BooleanState> {
@@ -34,7 +30,6 @@
private final AnimationIcon mDisable
= new AnimationIcon(R.drawable.ic_invert_colors_disable_animation);
private final SecureSetting mSetting;
- private final UsageTracker mUsageTracker;
private boolean mListening;
@@ -45,28 +40,14 @@
Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
- if (value != 0 || observedChange) {
- mUsageTracker.trackUsage();
- }
- if (mListening) {
- handleRefreshState(value);
- }
+ handleRefreshState(value);
}
};
- mUsageTracker = new UsageTracker(host.getContext(),
- Prefs.Key.COLOR_INVERSION_TILE_LAST_USED, ColorInversionTile.class,
- R.integer.days_to_show_color_inversion_tile);
- if (mSetting.getValue() != 0 && !mUsageTracker.isRecentlyUsed()) {
- mUsageTracker.trackUsage();
- }
- mUsageTracker.setListening(true);
- mSetting.setListening(true);
}
@Override
protected void handleDestroy() {
super.handleDestroy();
- mUsageTracker.setListening(false);
mSetting.setListening(false);
}
@@ -77,7 +58,7 @@
@Override
public void setListening(boolean listening) {
- mListening = listening;
+ mSetting.setListening(listening);
}
@Override
@@ -95,23 +76,9 @@
}
@Override
- protected void handleLongClick() {
- if (mState.value) return; // don't allow usage reset if inversion is active
- final String title = mContext.getString(R.string.quick_settings_reset_confirmation_title,
- mState.label);
- mUsageTracker.showResetConfirmation(title, new Runnable() {
- @Override
- public void run() {
- refreshState();
- }
- });
- }
-
- @Override
protected void handleUpdateState(BooleanState state, Object arg) {
final int value = arg instanceof Integer ? (Integer) arg : mSetting.getValue();
final boolean enabled = value != 0;
- state.visible = enabled || mUsageTracker.isRecentlyUsed() || QSPanel.isTheNewQS(mContext);
state.value = enabled;
state.label = mContext.getString(R.string.quick_settings_inversion_label);
state.icon = enabled ? mEnable : mDisable;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
index 04006eb..bb74f34 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
@@ -104,9 +104,15 @@
mServiceConnection, Service.BIND_AUTO_CREATE,
new UserHandle(ActivityManager.getCurrentUser()));
mBound = true;
+ } else {
+ if (mService != null) {
+ mService.onStartListening();
+ } else {
+ Log.d(TAG, "Can't start service listening");
+ }
}
} else {
- if (mService!= null) {
+ if (mService != null) {
mService.onStopListening();
}
if (mIsTokenGranted && !mIsShowingDialog) {
@@ -168,7 +174,6 @@
@Override
protected void handleUpdateState(State state, Object arg) {
- state.visible = true;
Drawable drawable = mTile.getIcon().loadDrawable(mContext);
drawable.setTint(mContext.getColor(android.R.color.white));
state.icon = new DrawableIcon(drawable);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 781ab1c..d96f735 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -29,7 +29,6 @@
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;
import android.widget.Toast;
-
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Prefs;
import com.android.systemui.R;
@@ -126,7 +125,6 @@
final boolean newValue = zen != Global.ZEN_MODE_OFF;
final boolean valueChanged = state.value != newValue;
state.value = newValue;
- state.visible = isVisible(mContext);
switch (zen) {
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
@@ -215,6 +213,10 @@
}
};
+ public static boolean isSupported(Host host) {
+ return isVisible(host.getContext());
+ }
+
private final class DndDetailAdapter implements DetailAdapter, OnAttachStateChangeListener {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 21cbef2..12c1298 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -17,7 +17,6 @@
package com.android.systemui.qs.tiles;
import android.app.ActivityManager;
-
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
@@ -71,7 +70,8 @@
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- state.visible = mFlashlightController.isAvailable();
+ // TODO: Flashlight available handling...
+// state.visible = mFlashlightController.isAvailable();
state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
if (arg instanceof UserBoolean) {
boolean value = ((UserBoolean) arg).value;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 79084ae..250d567 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -16,16 +16,9 @@
package com.android.systemui.qs.tiles;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.UsageTracker;
import com.android.systemui.statusbar.policy.HotspotController;
/** Quick settings tile: Hotspot **/
@@ -36,19 +29,15 @@
new AnimationIcon(R.drawable.ic_hotspot_disable_animation);
private final HotspotController mController;
private final Callback mCallback = new Callback();
- private final UsageTracker mUsageTracker;
public HotspotTile(Host host) {
super(host);
mController = host.getHotspotController();
- mUsageTracker = newUsageTracker(host.getContext());
- mUsageTracker.setListening(true);
}
@Override
protected void handleDestroy() {
super.handleDestroy();
- mUsageTracker.setListening(false);
}
@Override
@@ -75,22 +64,7 @@
}
@Override
- protected void handleLongClick() {
- if (mState.value) return; // don't allow usage reset if hotspot is active
- final String title = mContext.getString(R.string.quick_settings_reset_confirmation_title,
- mState.label);
- mUsageTracker.showResetConfirmation(title, new Runnable() {
- @Override
- public void run() {
- refreshState();
- }
- });
- }
-
- @Override
protected void handleUpdateState(BooleanState state, Object arg) {
- state.visible = (mController.isHotspotSupported() && mUsageTracker.isRecentlyUsed())
- || QSPanel.isTheNewQS(mContext);
state.label = mContext.getString(R.string.quick_settings_hotspot_label);
if (arg instanceof Boolean) {
@@ -98,7 +72,7 @@
} else {
state.value = mController.isHotspotEnabled();
}
- state.icon = state.visible && state.value ? mEnable : mDisable;
+ state.icon = state.value ? mEnable : mDisable;
}
@Override
@@ -115,31 +89,10 @@
}
}
- private static UsageTracker newUsageTracker(Context context) {
- return new UsageTracker(context, Prefs.Key.HOTSPOT_TILE_LAST_USED, HotspotTile.class,
- R.integer.days_to_show_hotspot_tile);
- }
-
private final class Callback implements HotspotController.Callback {
@Override
public void onHotspotChanged(boolean enabled) {
refreshState(enabled);
}
};
-
- /**
- * This will catch broadcasts for changes in hotspot state so we can show
- * the hotspot tile for a number of days after use.
- */
- public static class APChangedReceiver extends BroadcastReceiver {
- private UsageTracker mUsageTracker;
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mUsageTracker == null) {
- mUsageTracker = newUsageTracker(context);
- }
- mUsageTracker.trackUsage();
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
index c7f2284..0883445 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
@@ -124,7 +124,6 @@
}
// Save the last one in case we need it later.
mLastIntent = intent;
- state.visible = intent.getBooleanExtra("visible", true);
state.contentDescription = intent.getStringExtra("contentDescription");
state.label = intent.getStringExtra("label");
state.icon = null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 0e2672c..08540f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -18,7 +18,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
-import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.LocationController;
@@ -60,6 +59,20 @@
@Override
protected void handleClick() {
+ if (mKeyguard.isSecure() && mKeyguard.isShowing()) {
+ mHost.startRunnableDismissingKeyguard(new Runnable() {
+ @Override
+ public void run() {
+ final boolean wasEnabled = (Boolean) mState.value;
+ mHost.openPanels();
+ MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
+ mController.setLocationEnabled(!wasEnabled);
+ mEnable.setAllowAnimation(true);
+ mDisable.setAllowAnimation(true);
+ }
+ });
+ return;
+ }
final boolean wasEnabled = (Boolean) mState.value;
MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
mController.setLocationEnabled(!wasEnabled);
@@ -74,7 +87,6 @@
// Work around for bug 15916487: don't show location tile on top of lock screen. After the
// bug is fixed, this should be reverted to only hiding it on secure lock screens:
// state.visible = !(mKeyguard.isSecure() && mKeyguard.isShowing());
- state.visible = !mKeyguard.isShowing() || QSPanel.isTheNewQS(mContext);
state.value = locationEnabled;
if (locationEnabled) {
state.icon = mEnable;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QAirplaneTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QAirplaneTile.java
deleted file mode 100644
index b77191e..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QAirplaneTile.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2015 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.qs.tiles;
-
-import android.content.Context;
-import com.android.systemui.QSQuickTileView;
-import com.android.systemui.qs.QSTileBaseView;
-import com.android.systemui.qs.QSTileView;
-
-/** Quick settings tile: Airplane mode **/
-public class QAirplaneTile extends AirplaneModeTile {
-
- public QAirplaneTile(Host host) {
- super(host);
- }
-
- @Override
- public QSTileBaseView createTileView(Context context) {
- return new QSQuickTileView(context);
- }
-
- @Override
- public int getTileType() {
- return QSTileView.QS_TYPE_QUICK;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QBluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QBluetoothTile.java
deleted file mode 100644
index 4fe7e45..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QBluetoothTile.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2015 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.qs.tiles;
-
-import android.content.Context;
-import com.android.systemui.QSQuickTileView;
-import com.android.systemui.qs.QSTileBaseView;
-import com.android.systemui.qs.QSTileView;
-
-/** Quick settings tile: Bluetooth **/
-public class QBluetoothTile extends BluetoothTile {
-
- public QBluetoothTile(Host host) {
- super(host, false);
- }
-
- @Override
- public QSTileBaseView createTileView(Context context) {
- return new QSQuickTileView(context);
- }
-
- @Override
- public int getTileType() {
- return QSTileView.QS_TYPE_QUICK;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QFlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QFlashlightTile.java
deleted file mode 100644
index e115755e..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QFlashlightTile.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2015 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.qs.tiles;
-
-import android.content.Context;
-import com.android.systemui.QSQuickTileView;
-import com.android.systemui.qs.QSTileBaseView;
-import com.android.systemui.qs.QSTileView;
-
-/** Quick settings tile: Flashlight **/
-public class QFlashlightTile extends FlashlightTile {
-
- public QFlashlightTile(Host host) {
- super(host);
- }
-
- @Override
- public QSTileBaseView createTileView(Context context) {
- return new QSQuickTileView(context);
- }
-
- @Override
- public int getTileType() {
- return QSTileView.QS_TYPE_QUICK;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QLockTile.java
deleted file mode 100644
index 8b3013a..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QLockTile.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2015 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.qs.tiles;
-
-import android.content.Context;
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.QSQuickTileView;
-import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileBaseView;
-import com.android.systemui.qs.QSTileView;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-
-public class QLockTile extends QSTile<QSTile.State> implements KeyguardMonitor.Callback {
-
- private final KeyguardMonitor mKeyguard;
-
- public QLockTile(Host host) {
- super(host);
- mKeyguard = host.getKeyguardMonitor();
- }
-
- @Override
- public QSTileBaseView createTileView(Context context) {
- return new QSQuickTileView(context);
- }
-
- @Override
- public int getTileType() {
- return QSTileView.QS_TYPE_QUICK;
- }
-
- @Override
- protected State newTileState() {
- return new State();
- }
-
- @Override
- public void setListening(boolean listening) {
- if (listening) {
- mKeyguard.addCallback(this);
- } else {
- mKeyguard.removeCallback(this);
- }
- }
-
- @Override
- public int getMetricsCategory() {
- return MetricsLogger.QS_LOCK_TILE;
- }
-
- @Override
- public void onKeyguardChanged() {
- refreshState();
- }
-
- @Override
- protected void handleClick() {
- if (mKeyguard.isShowing()) {
- mKeyguard.unlock();
- } else {
- mKeyguard.lock();
- }
- }
-
- @Override
- protected void handleUpdateState(State state, Object arg) {
- // TOD: Content description.
- state.visible = true;
- if (mKeyguard.isShowing()) {
- state.icon = ResourceIcon.get(R.drawable.ic_qs_lock);
- } else {
- state.icon = ResourceIcon.get(R.drawable.ic_qs_lock_open);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRotationLockTile.java
deleted file mode 100644
index 5f5cab5..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRotationLockTile.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2015 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.qs.tiles;
-
-import android.content.Context;
-import com.android.systemui.QSQuickTileView;
-import com.android.systemui.qs.QSTileBaseView;
-import com.android.systemui.qs.QSTileView;
-
-/** Quick settings tile: Rotation **/
-public class QRotationLockTile extends RotationLockTile {
-
- public QRotationLockTile(Host host) {
- super(host);
- }
-
- @Override
- public QSTileBaseView createTileView(Context context) {
- return new QSQuickTileView(context);
- }
-
- @Override
- public int getTileType() {
- return QSTileView.QS_TYPE_QUICK;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java
deleted file mode 100644
index f0fe87d..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2015 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.qs.tiles;
-
-import android.content.Context;
-import com.android.systemui.QSQuickTileView;
-import com.android.systemui.qs.QSTileBaseView;
-import com.android.systemui.qs.QSTileView;
-import com.android.systemui.statusbar.policy.WifiIcons;
-
-/** Quick settings tile: Wifi **/
-public class QWifiTile extends WifiTile {
-
- public QWifiTile(Host host) {
- super(host, false);
- }
-
- @Override
- public QSTileBaseView createTileView(Context context) {
- return new QSQuickTileView(context);
- }
-
- @Override
- public int getTileType() {
- return QSTileView.QS_TYPE_QUICK;
- }
-
- @Override
- protected void handleUpdateState(SignalState state, Object arg) {
- super.handleUpdateState(state, arg);
-
- CallbackInfo cb = (CallbackInfo) arg;
- if (cb == null) {
- cb = mSignalCallback.mInfo;
- }
- boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null);
-
- if (state.enabled && wifiConnected) {
- // Only show full signal here.
- state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][4]);
- }
- // No activity in the quick toggle.
- state.activityIn = false;
- state.activityOut = false;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 1a26a4d..d85cf60 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -72,7 +72,8 @@
final boolean rotationLocked = arg != null ? ((UserBoolean) arg).value
: mController.isRotationLocked();
final boolean userInitiated = arg != null ? ((UserBoolean) arg).userInitiated : false;
- state.visible = mController.isRotationLockAffordanceVisible();
+ // TODO: Handle accessibility rotation lock and whatnot.
+// state.visible = mController.isRotationLockAffordanceVisible();
if (state.value == rotationLocked && state.contentDescription != null) {
// No change and initialized, no need to update all the values.
return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index 3c5ab8d..d29cae4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -67,17 +67,19 @@
@Override
protected void handleUpdateState(State state, Object arg) {
final Pair<String, Drawable> p = arg != null ? (Pair<String, Drawable>) arg : mLastUpdate;
- state.visible = p != null;
- if (!state.visible) return;
- state.label = p.first;
- // TODO: Better content description.
- state.contentDescription = p.first;
- state.icon = new Icon() {
- @Override
- public Drawable getDrawable(Context context) {
- return p.second;
- }
- };
+ if (p != null) {
+ state.label = p.first;
+ // TODO: Better content description.
+ state.contentDescription = p.first;
+ state.icon = new Icon() {
+ @Override
+ public Drawable getDrawable(Context context) {
+ return p.second;
+ }
+ };
+ } else {
+ // TODO: Default state.
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 7f4442a..48b4096 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -18,20 +18,19 @@
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
-
import com.android.internal.logging.MetricsLogger;
import com.android.settingslib.wifi.AccessPoint;
import com.android.systemui.R;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
+import com.android.systemui.qs.QSIconView;
import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileBaseView;
-import com.android.systemui.qs.QSTileView;
import com.android.systemui.qs.SignalTileView;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
@@ -51,22 +50,14 @@
protected final WifiSignalCallback mSignalCallback = new WifiSignalCallback();
- private final boolean mAlwaysDetail;
-
- public WifiTile(Host host, boolean alwaysDetail) {
+ public WifiTile(Host host) {
super(host);
- mAlwaysDetail = alwaysDetail;
mController = host.getNetworkController();
mWifiController = mController.getAccessPointController();
mDetailAdapter = new WifiDetailAdapter();
}
@Override
- public int getTileType() {
- return QSTileView.QS_TYPE_DUAL;
- }
-
- @Override
protected SignalState newTileState() {
return new SignalState();
}
@@ -95,23 +86,20 @@
}
@Override
- public QSTileBaseView createTileView(Context context) {
+ public QSIconView createTileView(Context context) {
return new SignalTileView(context);
}
@Override
- protected void handleClick() {
- if (mAlwaysDetail) {
- handleSecondaryClick();
- return;
- }
+ protected void handleSecondaryClick() {
+ // Secondary clicks are header clicks, just toggle.
mState.copyTo(mStateBeforeClick);
MetricsLogger.action(mContext, getMetricsCategory(), !mState.enabled);
mController.setWifiEnabled(!mState.enabled);
}
@Override
- protected void handleSecondaryClick() {
+ protected void handleClick() {
if (!mWifiController.canConfigWifi()) {
mHost.startActivityDismissingKeyguard(new Intent(Settings.ACTION_WIFI_SETTINGS));
return;
@@ -125,7 +113,6 @@
@Override
protected void handleUpdateState(SignalState state, Object arg) {
- state.visible = true;
if (DEBUG) Log.d(TAG, "handleUpdateState arg=" + arg);
CallbackInfo cb = (CallbackInfo) arg;
if (cb == null) {
@@ -201,6 +188,10 @@
return string;
}
+ public static boolean isSupported(Host host) {
+ return host.getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
+ }
+
protected static final class CallbackInfo {
boolean enabled;
boolean connected;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 7605f3b8..07915f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -22,14 +22,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
-import android.os.IUserManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
-import android.util.ArraySet;
-import android.util.Log;
-
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
@@ -110,8 +104,7 @@
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
if (!hasActiveProfile()) {
- state.visible = false;
- state.value = false;
+ mHost.removeTile(getTileSpec());
return;
}
@@ -124,7 +117,6 @@
userInitialized = false;
}
- state.visible = true;
final AnimationIcon icon;
state.label = mContext.getString(R.string.quick_settings_work_mode_label);
if (state.value) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index e51cf7ac..6dde157 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -2233,6 +2233,10 @@
mHandler.post(mAnimateCollapsePanels);
}
+ public void postAnimateOpenPanels() {
+ mHandler.sendEmptyMessage(MSG_OPEN_SETTINGS_PANEL);
+ }
+
public void animateCollapsePanels(int flags) {
animateCollapsePanels(flags, false /* force */, false /* delayed */,
1.0f /* speedUpFactor */);
@@ -3282,6 +3286,16 @@
return !isDeviceProvisioned() || (mDisabled1 & StatusBarManager.DISABLE_SEARCH) != 0;
}
+ public void postQSRunnableDismissingKeyguard(final Runnable runnable) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mLeaveOpenOnKeyguardHide = true;
+ executeRunnableDismissingKeyguard(runnable, null, false, true);
+ }
+ });
+ }
+
public void postStartActivityDismissingKeyguard(final PendingIntent intent) {
mHandler.post(new Runnable() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 327b81e..c740b08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.app.ActivityManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -27,10 +28,11 @@
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
+import android.provider.Settings;
import android.service.quicksettings.IQSService;
import android.service.quicksettings.Tile;
+import android.text.TextUtils;
import android.util.Log;
-
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.tiles.AirplaneModeTile;
@@ -45,12 +47,6 @@
import com.android.systemui.qs.tiles.HotspotTile;
import com.android.systemui.qs.tiles.IntentTile;
import com.android.systemui.qs.tiles.LocationTile;
-import com.android.systemui.qs.tiles.QAirplaneTile;
-import com.android.systemui.qs.tiles.QBluetoothTile;
-import com.android.systemui.qs.tiles.QFlashlightTile;
-import com.android.systemui.qs.tiles.QLockTile;
-import com.android.systemui.qs.tiles.QRotationLockTile;
-import com.android.systemui.qs.tiles.QWifiTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.qs.tiles.UserTile;
import com.android.systemui.qs.tiles.WifiTile;
@@ -166,6 +162,11 @@
}
@Override
+ public void startRunnableDismissingKeyguard(Runnable runnable) {
+ mStatusBar.postQSRunnableDismissingKeyguard(runnable);
+ }
+
+ @Override
public void warn(String message, Throwable t) {
// already logged
}
@@ -176,6 +177,11 @@
}
@Override
+ public void openPanels() {
+ mStatusBar.postAnimateOpenPanels();
+ }
+
+ @Override
public Looper getLooper() {
return mLooper;
}
@@ -273,8 +279,10 @@
if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
try {
QSTile<?> tile = createTile(tileSpec);
- tile.setTileSpec(tileSpec);
- newTiles.put(tileSpec, tile);
+ if (tile != null) {
+ tile.setTileSpec(tileSpec);
+ newTiles.put(tileSpec, tile);
+ }
} catch (Throwable t) {
Log.w(TAG, "Error creating tile for spec: " + tileSpec, t);
}
@@ -290,6 +298,14 @@
}
@Override
+ public void removeTile(String tileSpec) {
+ ArrayList<String> specs = new ArrayList<>(mTileSpecs);
+ specs.remove(tileSpec);
+ Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
+ TextUtils.join(",", specs), ActivityManager.getCurrentUser());
+ }
+
+ @Override
public void updateQsTile(Tile tile) throws RemoteException {
verifyCaller(tile.getComponentName().getPackageName());
CustomTile customTile = getTileForComponent(tile.getComponentName());
@@ -334,13 +350,17 @@
}
public QSTile<?> createTile(String tileSpec) {
- if (tileSpec.equals("wifi")) return new WifiTile(this, false);
- else if (tileSpec.equals("bt")) return new BluetoothTile(this, false);
+ if (tileSpec.equals("wifi")) return WifiTile.isSupported(this)
+ ? new WifiTile(this) : null;
+ else if (tileSpec.equals("bt")) return BluetoothTile.isSupported(this)
+ ? new BluetoothTile(this) : null;
+ else if (tileSpec.equals("cell")) return CellularTile.isSupported(this)
+ ? new CellularTile(this) : null;
+ else if (tileSpec.equals("dnd")) return DndTile.isSupported(this)
+ ? new DndTile(this) : null;
else if (tileSpec.equals("inversion")) return new ColorInversionTile(this);
- else if (tileSpec.equals("cell")) return new CellularTile(this);
else if (tileSpec.equals("airplane")) return new AirplaneModeTile(this);
else if (tileSpec.equals("work")) return new WorkModeTile(this);
- else if (tileSpec.equals("dnd")) return new DndTile(this);
else if (tileSpec.equals("rotation")) return new RotationLockTile(this);
else if (tileSpec.equals("flashlight")) return new FlashlightTile(this);
else if (tileSpec.equals("location")) return new LocationTile(this);
@@ -348,16 +368,6 @@
else if (tileSpec.equals("hotspot")) return new HotspotTile(this);
else if (tileSpec.equals("user")) return new UserTile(this);
else if (tileSpec.equals("battery")) return new BatteryTile(this);
- // Detail only versions of wifi and bluetooth.
- else if (tileSpec.equals("dwifi")) return new WifiTile(this, true);
- else if (tileSpec.equals("dbt")) return new BluetoothTile(this, true);
- // Quick tiles, no text.
- else if (tileSpec.equals("qwifi")) return new QWifiTile(this);
- else if (tileSpec.equals("qbt")) return new QBluetoothTile(this);
- else if (tileSpec.equals("qairplane")) return new QAirplaneTile(this);
- else if (tileSpec.equals("qrotation")) return new QRotationLockTile(this);
- else if (tileSpec.equals("qflashlight")) return new QFlashlightTile(this);
- else if (tileSpec.equals("qlock")) return new QLockTile(this);
// Intent tiles.
else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec);
else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(this,tileSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index cc9f5c7..26ff97a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -30,10 +30,10 @@
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
-
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.UserInfoController;
@@ -63,7 +63,7 @@
private boolean mListening;
private AlarmManager.AlarmClockInfo mNextAlarm;
- private QSPanel mHeaderQsPanel;
+ private QuickQSPanel mHeaderQsPanel;
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -75,7 +75,7 @@
mExpandedGroup = (ViewGroup) findViewById(R.id.expanded_group);
- mHeaderQsPanel = (QSPanel) findViewById(R.id.quick_qs_panel);
+ mHeaderQsPanel = (QuickQSPanel) findViewById(R.id.quick_qs_panel);
mSettingsButton = (SettingsButton) findViewById(R.id.settings_button);
mSettingsContainer = findViewById(R.id.settings_button_container);
@@ -133,9 +133,10 @@
@Override
public void setExpansion(float headerExpansionFraction) {
- float offset = getHeight() * headerExpansionFraction;
- mExpandedGroup.setTranslationY(offset - getHeight());
- mHeaderQsPanel.setTranslationY(offset);
+ mExpandedGroup.setAlpha(headerExpansionFraction);
+ mExpandedGroup.setVisibility(headerExpansionFraction > 0 ? View.VISIBLE : View.INVISIBLE);
+ mHeaderQsPanel.setAlpha(1 - headerExpansionFraction);
+ mHeaderQsPanel.setVisibility(headerExpansionFraction < 1 ? View.VISIBLE : View.INVISIBLE);
}
public void setListening(boolean listening) {
@@ -190,7 +191,9 @@
host.getUserSwitcherController(), host.getUserInfoController(),
host.getKeyguardMonitor(), host.getSecurityController(),
host.getBatteryController());
+ mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
mHeaderQsPanel.setHost(myHost);
+ mHeaderQsPanel.setMaxTiles(3);
mHeaderQsPanel.setTiles(myHost.getTiles());
myHost.addCallback(new QSTile.Host.Callback() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java
index 04a51f0..d19a825 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java
@@ -9,8 +9,8 @@
public class QSPagingSwitch extends TunerSwitch {
public static final String QS_PAGE_TILES =
- "dwifi,dbt,dnd,cell,battery,user,rotation,flashlight,location,"
- + "hotspot,qwifi,qbt,qlock,qflashlight,qairplane,inversion,cast";
+ "dnd,cell,battery,user,rotation,flashlight,location,"
+ + "hotspot,inversion,cast";
public QSPagingSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index f6af942..960fb4b 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1363,7 +1363,7 @@
pw.print(" nowRTC="); pw.print(nowRTC);
pw.print("="); pw.print(sdf.format(new Date(nowRTC)));
- pw.print(" nowELAPSED="); TimeUtils.formatDuration(nowELAPSED, pw);
+ pw.print(" nowELAPSED="); pw.print(nowELAPSED);
pw.println();
pw.print(" mLastTimeChangeClockTime="); pw.print(mLastTimeChangeClockTime);
pw.print("="); pw.println(sdf.format(new Date(mLastTimeChangeClockTime)));
@@ -1464,6 +1464,15 @@
pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount);
pw.println();
+ if (mInFlight.size() > 0) {
+ pw.println("Outstanding deliveries:");
+ for (int i = 0; i < mInFlight.size(); i++) {
+ pw.print(" #"); pw.print(i); pw.print(": ");
+ pw.println(mInFlight.get(i));
+ }
+ pw.println();
+ }
+
pw.print(" mAllowWhileIdleMinTime=");
TimeUtils.formatDuration(mAllowWhileIdleMinTime, pw);
pw.println();
@@ -2956,6 +2965,11 @@
// is a repeating alarm, so toss it
removeImpl(alarm.operation);
}
+ // No actual delivery was possible, so the delivery tracker's
+ // 'finished' callback won't be invoked. We also don't need
+ // to do any wakelock or stats tracking, so we have nothing
+ // left to do here but go on to the next thing.
+ return;
}
} else {
// Direct listener callback alarm
@@ -2974,6 +2988,11 @@
Slog.i(TAG, "Alarm undeliverable to listener "
+ alarm.listener.asBinder(), e);
}
+ // As in the PendingIntent.CanceledException case, delivery of the
+ // alarm was not possible, so we have no wakelock or timeout or
+ // stats management to do. It threw before we posted the delayed
+ // timeout message, so we're done here.
+ return;
}
}
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 473eff6..184f890 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2716,12 +2716,13 @@
}
@Override
- public void createUserKey(int userId, int serialNumber) {
+ public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
waitForReady();
try {
- mCryptConnector.execute("cryptfs", "create_user_key", userId, serialNumber);
+ mCryptConnector.execute("cryptfs", "create_user_key", userId, serialNumber,
+ ephemeral ? 1 : 0);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
@@ -2797,13 +2798,14 @@
}
@Override
- public void prepareUserStorage(String volumeUuid, int userId, int serialNumber) {
+ public void prepareUserStorage(
+ String volumeUuid, int userId, int serialNumber, boolean ephemeral) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
waitForReady();
try {
mCryptConnector.execute("cryptfs", "prepare_user_storage", escapeNull(volumeUuid),
- userId, serialNumber);
+ userId, serialNumber, ephemeral ? 1 : 0);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 2742c65..4348913 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1240,8 +1240,9 @@
try {
// Before going further -- if this app is not allowed to run in the
// background, then at this point we aren't going to let it period.
- if (!mAm.checkAllowBackgroundLocked(sInfo.applicationInfo.uid,
- sInfo.packageName, callingPid)) {
+ final int allowed = mAm.checkAllowBackgroundLocked(
+ sInfo.applicationInfo.uid, sInfo.packageName, callingPid);
+ if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
Slog.w(TAG, "Background execution not allowed: service "
+ r.intent + " to " + name.flattenToShortString()
+ " from pid=" + callingPid + " uid=" + callingUid
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f22062c..507233f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7463,13 +7463,11 @@
public int getAppStartMode(int uid, String packageName) {
synchronized (this) {
- boolean bg = checkAllowBackgroundLocked(uid, packageName, -1);
- return bg ? ActivityManager.APP_START_MODE_NORMAL
- : ActivityManager.APP_START_MODE_DISABLED;
+ return checkAllowBackgroundLocked(uid, packageName, -1);
}
}
- boolean checkAllowBackgroundLocked(int uid, String packageName, int callingPid) {
+ int checkAllowBackgroundLocked(int uid, String packageName, int callingPid) {
UidRecord uidRec = mActiveUids.get(uid);
if (uidRec == null || uidRec.idle) {
if (callingPid >= 0) {
@@ -7480,15 +7478,15 @@
if (proc != null && proc.curProcState < ActivityManager.PROCESS_STATE_RECEIVER) {
// Whoever is instigating this is in the foreground, so we will allow it
// to go through.
- return true;
+ return ActivityManager.APP_START_MODE_NORMAL;
}
}
if (mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName)
!= AppOpsManager.MODE_ALLOWED) {
- return false;
+ return ActivityManager.APP_START_MODE_DELAYED;
}
}
- return true;
+ return ActivityManager.APP_START_MODE_NORMAL;
}
private ProviderInfo getProviderInfoLocked(String authority, int userHandle) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index b160981..2fb71c3 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -562,8 +562,9 @@
skip = true;
}
if (!skip) {
- if (!mService.checkAllowBackgroundLocked(filter.receiverList.uid, filter.packageName,
- -1)) {
+ final int allowed = mService.checkAllowBackgroundLocked(filter.receiverList.uid,
+ filter.packageName, -1);
+ if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
Slog.w(TAG, "Background execution not allowed: receiving "
+ r.intent
+ " to " + filter.receiverList.app
@@ -1013,15 +1014,6 @@
skip = true;
}
if (!skip) {
- if (!mService.checkAllowBackgroundLocked(info.activityInfo.applicationInfo.uid,
- info.activityInfo.packageName, -1)) {
- Slog.w(TAG, "Background execution not allowed: receiving "
- + r.intent + " to "
- + component.flattenToShortString());
- skip = true;
- }
- }
- if (!skip) {
skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
}
@@ -1083,6 +1075,28 @@
}
}
+ String targetProcess = info.activityInfo.processName;
+ ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
+ info.activityInfo.applicationInfo.uid, false);
+ if (!skip) {
+ final int allowed = mService.checkAllowBackgroundLocked(
+ info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1);
+ if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
+ // We won't allow this receiver to be launched if the app has been
+ // completely disabled from launches, or it is delayed and the broadcast
+ // was not explicitly sent to it and this would result in a new process
+ // for it being created.
+ if (allowed == ActivityManager.APP_START_MODE_DISABLED
+ || (r.intent.getComponent() == null
+ && r.intent.getPackage() == null && app == null)) {
+ Slog.w(TAG, "Background execution not allowed: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString());
+ skip = true;
+ }
+ }
+ }
+
if (skip) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Skipping delivery of ordered [" + mQueueName + "] "
@@ -1095,7 +1109,6 @@
}
r.state = BroadcastRecord.APP_RECEIVE;
- String targetProcess = info.activityInfo.processName;
r.curComponent = component;
final int receiverUid = info.activityInfo.applicationInfo.uid;
// If it's a singleton, it needs to be the same app or a special app
@@ -1126,8 +1139,6 @@
}
// Is this receiver's application already running?
- ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
- info.activityInfo.applicationInfo.uid, false);
if (app != null && app.thread != null) {
try {
app.addPackage(info.activityInfo.packageName,
diff --git a/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java b/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
new file mode 100644
index 0000000..8786350
--- /dev/null
+++ b/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
@@ -0,0 +1,698 @@
+/*
+ * Copyright (C) 2015 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.pm;
+
+import android.content.Context;
+import android.content.pm.EphemeralApplicationInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Binder;
+import android.os.Environment;
+import android.provider.Settings;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+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.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class is a part of the package manager service that is responsible
+ * for managing data associated with ephemeral apps such as cached uninstalled
+ * ephemeral apps and ephemeral apps' cookies.
+ */
+class EphemeralApplicationRegistry {
+ private static final boolean DEBUG = false;
+
+ private static final String LOG_TAG = "EphemeralAppRegistry";
+
+ private static final long DEFAULT_UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS =
+ DEBUG ? 60 * 1000L /* one min */ : 30 * 24 * 60 * 60 * 1000L; /* one month */
+
+ private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
+
+ private static final String EPHEMERAL_APPS_FOLDER = "ephemeral";
+ private static final String EPHEMERAL_APP_ICON_FILE = "icon.png";
+ private static final String EPHEMERAL_APP_COOKIE_FILE_PREFIX = "cookie_";
+ private static final String EPHEMERAL_APP_COOKIE_FILE_SIFFIX = ".dat";
+ private static final String EPHEMERAL_APP_METADATA_FILE = "metadata.xml";
+
+ private static final String TAG_PACKAGE = "package";
+ private static final String TAG_PERMS = "perms";
+ private static final String TAG_PERM = "perm";
+
+ private static final String ATTR_LABEL = "label";
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_GRANTED = "granted";
+
+ private final PackageManagerService mService;
+
+ @GuardedBy("mService.mPackages")
+ private SparseArray<List<UninstalledEphemeralAppState>> mUninstalledEphemeralApps;
+
+ public EphemeralApplicationRegistry(PackageManagerService service) {
+ mService = service;
+ }
+
+ public byte[] getEphemeralApplicationCookieLPw(String packageName, int userId) {
+ pruneUninstalledEphemeralAppsLPw(userId);
+
+ File cookieFile = peekEphemeralCookieFile(packageName, userId);
+ if (cookieFile != null && cookieFile.exists()) {
+ try {
+ return IoUtils.readFileAsByteArray(cookieFile.toString());
+ } catch (IOException e) {
+ Slog.w(LOG_TAG, "Error reading cookie file: " + cookieFile);
+ }
+ }
+ return null;
+ }
+
+ public boolean setEphemeralApplicationCookieLPw(String packageName,
+ byte[] cookie, int userId) {
+ pruneUninstalledEphemeralAppsLPw(userId);
+
+ PackageParser.Package pkg = mService.mPackages.get(packageName);
+ if (pkg == null) {
+ return false;
+ }
+
+ if (!isValidCookie(mService.mContext, cookie)) {
+ return false;
+ }
+
+ File appDir = getEphemeralApplicationDir(pkg.packageName, userId);
+ if (!appDir.exists() && !appDir.mkdirs()) {
+ return false;
+ }
+
+ File cookieFile = computeEphemeralCookieFile(pkg, userId);
+ if (cookieFile.exists() && !cookieFile.delete()) {
+ return false;
+ }
+
+ try (FileOutputStream fos = new FileOutputStream(cookieFile)) {
+ fos.write(cookie, 0, cookie.length);
+ } catch (IOException e) {
+ Slog.w(LOG_TAG, "Error writing cookie file: " + cookieFile);
+ return false;
+ }
+ return true;
+ }
+
+ public Bitmap getEphemeralApplicationIconLPw(String packageName, int userId) {
+ pruneUninstalledEphemeralAppsLPw(userId);
+
+ File iconFile = new File(getEphemeralApplicationDir(packageName, userId),
+ EPHEMERAL_APP_ICON_FILE);
+ if (iconFile.exists()) {
+ return BitmapFactory.decodeFile(iconFile.toString());
+ }
+ return null;
+ }
+
+ public List<EphemeralApplicationInfo> getEphemeralApplicationsLPw(int userId) {
+ pruneUninstalledEphemeralAppsLPw(userId);
+
+ List<EphemeralApplicationInfo> result = getInstalledEphemeralApplicationsLPr(userId);
+ result.addAll(getUninstalledEphemeralApplicationsLPr(userId));
+ return result;
+ }
+
+ public void onPackageInstalledLPw(PackageParser.Package pkg) {
+ PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps == null) {
+ return;
+ }
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ pruneUninstalledEphemeralAppsLPw(userId);
+
+ // Ignore not installed apps
+ if (mService.mPackages.get(pkg.packageName) == null || !ps.getInstalled(userId)) {
+ continue;
+ }
+
+ // Propagate permissions before removing any state
+ propagateEphemeralAppPermissionsIfNeeded(pkg, userId);
+
+ // Remove the in-memory state
+ if (mUninstalledEphemeralApps != null) {
+ List<UninstalledEphemeralAppState> uninstalledAppStates =
+ mUninstalledEphemeralApps.get(userId);
+ if (uninstalledAppStates != null) {
+ final int appCount = uninstalledAppStates.size();
+ for (int i = 0; i < appCount; i++) {
+ UninstalledEphemeralAppState uninstalledAppState =
+ uninstalledAppStates.get(i);
+ if (uninstalledAppState.mEphemeralApplicationInfo
+ .getPackageName().equals(pkg.packageName)) {
+ uninstalledAppStates.remove(i);
+ break;
+ }
+ }
+ }
+ }
+
+ // Remove the on-disk state except the cookie
+ File ephemeralAppDir = getEphemeralApplicationDir(pkg.packageName, userId);
+ new File(ephemeralAppDir, EPHEMERAL_APP_METADATA_FILE).delete();
+ new File(ephemeralAppDir, EPHEMERAL_APP_ICON_FILE).delete();
+
+ // If app signature changed - wipe the cookie
+ File currentCookieFile = peekEphemeralCookieFile(pkg.packageName, userId);
+ if (currentCookieFile == null) {
+ continue;
+ }
+ File expectedCookeFile = computeEphemeralCookieFile(pkg, userId);
+ if (!currentCookieFile.equals(expectedCookeFile)) {
+ Slog.i(LOG_TAG, "Signature for package " + pkg.packageName
+ + " changed - dropping cookie");
+ currentCookieFile.delete();
+ }
+ }
+ }
+
+ public void onPackageUninstalledLPw(PackageParser.Package pkg) {
+ PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps == null) {
+ return;
+ }
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ pruneUninstalledEphemeralAppsLPw(userId);
+
+ if (mService.mPackages.get(pkg.packageName) != null && ps.getInstalled(userId)) {
+ continue;
+ }
+
+ if (pkg.applicationInfo.isEphemeralApp()) {
+ // Add a record for an uninstalled ephemeral app
+ addUninstalledEphemeralAppLPw(pkg, userId);
+ } else {
+ // Deleting an app prunes all ephemeral state such as cookie
+ deleteDir(getEphemeralApplicationDir(pkg.packageName, userId));
+ }
+ }
+ }
+
+ public void onUserRemovedLPw(int userId) {
+ if (mUninstalledEphemeralApps != null) {
+ mUninstalledEphemeralApps.remove(userId);
+ }
+ deleteDir(getEphemeralApplicationsDir(userId));
+ }
+
+ private void addUninstalledEphemeralAppLPw(PackageParser.Package pkg, int userId) {
+ EphemeralApplicationInfo uninstalledApp = createEphemeralAppInfoForPackage(pkg, userId);
+ if (uninstalledApp == null) {
+ return;
+ }
+ if (mUninstalledEphemeralApps == null) {
+ mUninstalledEphemeralApps = new SparseArray<>();
+ }
+ List<UninstalledEphemeralAppState> uninstalledAppStates =
+ mUninstalledEphemeralApps.get(userId);
+ if (uninstalledAppStates == null) {
+ uninstalledAppStates = new ArrayList<>();
+ mUninstalledEphemeralApps.put(userId, uninstalledAppStates);
+ }
+ UninstalledEphemeralAppState uninstalledAppState = new UninstalledEphemeralAppState(
+ uninstalledApp, System.currentTimeMillis());
+ uninstalledAppStates.add(uninstalledAppState);
+
+ writeUninstalledEphemeralAppMetadata(uninstalledApp, userId);
+ writeEphemeralApplicationIconLPw(pkg, userId);
+ }
+
+ private void writeEphemeralApplicationIconLPw(PackageParser.Package pkg, int userId) {
+ File appDir = getEphemeralApplicationDir(pkg.packageName, userId);
+ if (!appDir.exists()) {
+ return;
+ }
+
+ Drawable icon = pkg.applicationInfo.loadIcon(mService.mContext.getPackageManager());
+
+ final Bitmap bitmap;
+ if (icon instanceof BitmapDrawable) {
+ bitmap = ((BitmapDrawable) icon).getBitmap();
+ } else {
+ bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(),
+ icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ icon.draw(canvas);
+ }
+
+ File iconFile = new File(getEphemeralApplicationDir(pkg.packageName, userId),
+ EPHEMERAL_APP_ICON_FILE);
+
+ try (FileOutputStream out = new FileOutputStream(iconFile)) {
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+ } catch (Exception e) {
+ Slog.e(LOG_TAG, "Error writing ephemeral app icon", e);
+ }
+ }
+
+ private void pruneUninstalledEphemeralAppsLPw(int userId) {
+ final long maxCacheDurationMillis = Settings.Global.getLong(
+ mService.mContext.getContentResolver(),
+ Settings.Global.UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS,
+ DEFAULT_UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS);
+
+ // Prune in-memory state
+ if (mUninstalledEphemeralApps != null) {
+ List<UninstalledEphemeralAppState> uninstalledAppStates =
+ mUninstalledEphemeralApps.get(userId);
+ if (uninstalledAppStates != null) {
+ final int appCount = uninstalledAppStates.size();
+ for (int j = appCount - 1; j >= 0; j--) {
+ UninstalledEphemeralAppState uninstalledAppState = uninstalledAppStates.get(j);
+ final long elapsedCachingMillis = System.currentTimeMillis()
+ - uninstalledAppState.mTimestamp;
+ if (elapsedCachingMillis > maxCacheDurationMillis) {
+ uninstalledAppStates.remove(j);
+ }
+ }
+ if (uninstalledAppStates.isEmpty()) {
+ mUninstalledEphemeralApps.remove(userId);
+ }
+ }
+ }
+
+ // Prune on-disk state
+ File ephemeralAppsDir = getEphemeralApplicationsDir(userId);
+ if (!ephemeralAppsDir.exists()) {
+ return;
+ }
+ File[] files = ephemeralAppsDir.listFiles();
+ if (files == null) {
+ return;
+ }
+ for (File ephemeralDir : files) {
+ if (!ephemeralDir.isDirectory()) {
+ continue;
+ }
+
+ File metadataFile = new File(ephemeralDir, EPHEMERAL_APP_METADATA_FILE);
+ if (!metadataFile.exists()) {
+ continue;
+ }
+
+ final long elapsedCachingMillis = System.currentTimeMillis()
+ - metadataFile.lastModified();
+ if (elapsedCachingMillis > maxCacheDurationMillis) {
+ deleteDir(ephemeralDir);
+ }
+ }
+ }
+
+ private List<EphemeralApplicationInfo> getInstalledEphemeralApplicationsLPr(int userId) {
+ List<EphemeralApplicationInfo> result = null;
+
+ final int packageCount = mService.mPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ PackageParser.Package pkg = mService.mPackages.valueAt(i);
+ if (!pkg.applicationInfo.isEphemeralApp()) {
+ continue;
+ }
+ EphemeralApplicationInfo info = createEphemeralAppInfoForPackage(pkg, userId);
+ if (info == null) {
+ continue;
+ }
+ if (result == null) {
+ result = new ArrayList<>();
+ }
+ result.add(info);
+ }
+
+ return result;
+ }
+
+ private EphemeralApplicationInfo createEphemeralAppInfoForPackage(
+ PackageParser.Package pkg, int userId) {
+ PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps == null) {
+ return null;
+ }
+ PackageUserState userState = ps.readUserState(userId);
+ if (userState == null || !userState.installed || userState.hidden) {
+ return null;
+ }
+
+ String[] requestedPermissions = new String[pkg.requestedPermissions.size()];
+ pkg.requestedPermissions.toArray(requestedPermissions);
+
+ Set<String> permissions = ps.getPermissionsState().getPermissions(userId);
+ String[] grantedPermissions = new String[permissions.size()];
+ permissions.toArray(grantedPermissions);
+
+ return new EphemeralApplicationInfo(pkg.applicationInfo,
+ requestedPermissions, grantedPermissions);
+ }
+
+ private List<EphemeralApplicationInfo> getUninstalledEphemeralApplicationsLPr(int userId) {
+ List<UninstalledEphemeralAppState> uninstalledAppStates =
+ getUninstalledEphemeralAppStatesLPr(userId);
+ if (uninstalledAppStates == null || uninstalledAppStates.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ List<EphemeralApplicationInfo> uninstalledApps = new ArrayList<>();
+ final int stateCount = uninstalledAppStates.size();
+ for (int i = 0; i < stateCount; i++) {
+ UninstalledEphemeralAppState uninstalledAppState = uninstalledAppStates.get(i);
+ uninstalledApps.add(uninstalledAppState.mEphemeralApplicationInfo);
+ }
+ return uninstalledApps;
+ }
+
+ private void propagateEphemeralAppPermissionsIfNeeded(PackageParser.Package pkg, int userId) {
+ EphemeralApplicationInfo appInfo = getOrParseUninstalledEphemeralAppInfo(pkg.packageName, userId);
+ if (appInfo == null) {
+ return;
+ }
+ if (ArrayUtils.isEmpty(appInfo.getGrantedPermissions())) {
+ return;
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ for (String grantedPermission : appInfo.getGrantedPermissions()) {
+ mService.grantRuntimePermission(pkg.packageName, grantedPermission, userId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private EphemeralApplicationInfo getOrParseUninstalledEphemeralAppInfo(String packageName,
+ int userId) {
+ if (mUninstalledEphemeralApps != null) {
+ List<UninstalledEphemeralAppState> uninstalledAppStates =
+ mUninstalledEphemeralApps.get(userId);
+ if (uninstalledAppStates != null) {
+ final int appCount = uninstalledAppStates.size();
+ for (int i = 0; i < appCount; i++) {
+ UninstalledEphemeralAppState uninstalledAppState = uninstalledAppStates.get(i);
+ if (uninstalledAppState.mEphemeralApplicationInfo
+ .getPackageName().equals(packageName)) {
+ return uninstalledAppState.mEphemeralApplicationInfo;
+ }
+ }
+ }
+ }
+
+ File metadataFile = new File(getEphemeralApplicationDir(packageName, userId),
+ EPHEMERAL_APP_METADATA_FILE);
+ UninstalledEphemeralAppState uninstalledAppState = parseMetadataFile(metadataFile);
+ if (uninstalledAppState == null) {
+ return null;
+ }
+
+ return uninstalledAppState.mEphemeralApplicationInfo;
+ }
+
+ private List<UninstalledEphemeralAppState> getUninstalledEphemeralAppStatesLPr(int userId) {
+ List<UninstalledEphemeralAppState> uninstalledAppStates = null;
+ if (mUninstalledEphemeralApps != null) {
+ uninstalledAppStates = mUninstalledEphemeralApps.get(userId);
+ if (uninstalledAppStates != null) {
+ return uninstalledAppStates;
+ }
+ }
+
+ File ephemeralAppsDir = getEphemeralApplicationsDir(userId);
+ if (ephemeralAppsDir.exists()) {
+ File[] files = ephemeralAppsDir.listFiles();
+ if (files != null) {
+ for (File ephemeralDir : files) {
+ if (!ephemeralDir.isDirectory()) {
+ continue;
+ }
+ File metadataFile = new File(ephemeralDir,
+ EPHEMERAL_APP_METADATA_FILE);
+ UninstalledEphemeralAppState uninstalledAppState =
+ parseMetadataFile(metadataFile);
+ if (uninstalledAppState == null) {
+ continue;
+ }
+ if (uninstalledAppStates == null) {
+ uninstalledAppStates = new ArrayList<>();
+ }
+ uninstalledAppStates.add(uninstalledAppState);
+ }
+ }
+ }
+
+ if (uninstalledAppStates != null) {
+ if (mUninstalledEphemeralApps == null) {
+ mUninstalledEphemeralApps = new SparseArray<>();
+ }
+ mUninstalledEphemeralApps.put(userId, uninstalledAppStates);
+ }
+
+ return uninstalledAppStates;
+ }
+
+ private static boolean isValidCookie(Context context, byte[] cookie) {
+ if (ArrayUtils.isEmpty(cookie)) {
+ return true;
+ }
+ return cookie.length <= context.getPackageManager().getEphemeralCookieMaxSizeBytes();
+ }
+
+ private static UninstalledEphemeralAppState parseMetadataFile(File metadataFile) {
+ if (!metadataFile.exists()) {
+ return null;
+ }
+ FileInputStream in;
+ try {
+ in = new AtomicFile(metadataFile).openRead();
+ } catch (FileNotFoundException fnfe) {
+ Slog.i(LOG_TAG, "No ephemeral metadata file");
+ return null;
+ }
+
+ final File ephemeralDir = metadataFile.getParentFile();
+ final long timestamp = metadataFile.lastModified();
+ final String packageName = ephemeralDir.getName();
+
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, StandardCharsets.UTF_8.name());
+ return new UninstalledEphemeralAppState(
+ parseMetadata(parser, packageName), timestamp);
+ } catch (XmlPullParserException | IOException e) {
+ throw new IllegalStateException("Failed parsing ephemeral"
+ + " metadata file: " + metadataFile, e);
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ private static File computeEphemeralCookieFile(PackageParser.Package pkg, int userId) {
+ File appDir = getEphemeralApplicationDir(pkg.packageName, userId);
+ String cookieFile = EPHEMERAL_APP_COOKIE_FILE_PREFIX + computePackageCertDigest(pkg)
+ + EPHEMERAL_APP_COOKIE_FILE_SIFFIX;
+ return new File(appDir, cookieFile);
+ }
+
+ private static File peekEphemeralCookieFile(String packageName, int userId) {
+ File appDir = getEphemeralApplicationDir(packageName, userId);
+ if (!appDir.exists()) {
+ return null;
+ }
+ for (File file : appDir.listFiles()) {
+ if (!file.isDirectory()
+ && file.getName().startsWith(EPHEMERAL_APP_COOKIE_FILE_PREFIX)
+ && file.getName().endsWith(EPHEMERAL_APP_COOKIE_FILE_SIFFIX)) {
+ return file;
+ }
+ }
+ return null;
+ }
+
+ private static EphemeralApplicationInfo parseMetadata(XmlPullParser parser, String packageName)
+ throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if (TAG_PACKAGE.equals(parser.getName())) {
+ return parsePackage(parser, packageName);
+ }
+ }
+ return null;
+ }
+
+ private static EphemeralApplicationInfo parsePackage(XmlPullParser parser, String packageName)
+ throws IOException, XmlPullParserException {
+ String label = parser.getAttributeValue(null, ATTR_LABEL);
+
+ List<String> outRequestedPermissions = new ArrayList<>();
+ List<String> outGrantedPermissions = new ArrayList<>();
+
+ final int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if (TAG_PERMS.equals(parser.getName())) {
+ parsePermissions(parser, outRequestedPermissions, outGrantedPermissions);
+ }
+ }
+
+ String[] requestedPermissions = new String[outRequestedPermissions.size()];
+ outRequestedPermissions.toArray(requestedPermissions);
+
+ String[] grantedPermissions = new String[outGrantedPermissions.size()];
+ outGrantedPermissions.toArray(grantedPermissions);
+
+ return new EphemeralApplicationInfo(packageName, label,
+ requestedPermissions, grantedPermissions);
+ }
+
+ private static void parsePermissions(XmlPullParser parser, List<String> outRequestedPermissions,
+ List<String> outGrantedPermissions) throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser,outerDepth)) {
+ if (TAG_PERM.equals(parser.getName())) {
+ String permission = XmlUtils.readStringAttribute(parser, ATTR_NAME);
+ outRequestedPermissions.add(permission);
+ if (XmlUtils.readBooleanAttribute(parser, ATTR_GRANTED)) {
+ outGrantedPermissions.add(permission);
+ }
+ }
+ }
+ }
+
+ private void writeUninstalledEphemeralAppMetadata(
+ EphemeralApplicationInfo ephemeralApp, int userId) {
+ File appDir = getEphemeralApplicationDir(ephemeralApp.getPackageName(), userId);
+ if (!appDir.exists() && !appDir.mkdirs()) {
+ return;
+ }
+
+ File metadataFile = new File(appDir, EPHEMERAL_APP_METADATA_FILE);
+
+ AtomicFile destination = new AtomicFile(metadataFile);
+ FileOutputStream out = null;
+ try {
+ out = destination.startWrite();
+
+ XmlSerializer serializer = Xml.newSerializer();
+ serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ serializer.startDocument(null, true);
+
+ serializer.startTag(null, TAG_PACKAGE);
+ serializer.attribute(null, ATTR_LABEL, ephemeralApp.loadLabel(
+ mService.mContext.getPackageManager()).toString());
+
+ serializer.startTag(null, TAG_PERMS);
+ for (String permission : ephemeralApp.getRequestedPermissions()) {
+ serializer.startTag(null, TAG_PERM);
+ serializer.attribute(null, ATTR_NAME, permission);
+ if (ArrayUtils.contains(ephemeralApp.getGrantedPermissions(), permission)) {
+ serializer.attribute(null, ATTR_GRANTED, String.valueOf(true));
+ }
+ serializer.endTag(null, TAG_PERM);
+ }
+ serializer.endTag(null, TAG_PERMS);
+
+ serializer.endTag(null, TAG_PACKAGE);
+
+ serializer.endDocument();
+ destination.finishWrite(out);
+ } catch (Throwable t) {
+ Slog.wtf(LOG_TAG, "Failed to write ephemeral state, restoring backup", t);
+ destination.failWrite(out);
+ } finally {
+ IoUtils.closeQuietly(out);
+ }
+ }
+
+ private static String computePackageCertDigest(PackageParser.Package pkg) {
+ MessageDigest messageDigest;
+ try {
+ messageDigest = MessageDigest.getInstance("SHA256");
+ } catch (NoSuchAlgorithmException e) {
+ /* can't happen */
+ return null;
+ }
+
+ messageDigest.update(pkg.mSignatures[0].toByteArray());
+
+ final byte[] digest = messageDigest.digest();
+ final int digestLength = digest.length;
+ final int charCount = 2 * digestLength;
+
+ final char[] chars = new char[charCount];
+ for (int i = 0; i < digestLength; i++) {
+ final int byteHex = digest[i] & 0xFF;
+ chars[i * 2] = HEX_ARRAY[byteHex >>> 4];
+ chars[i * 2 + 1] = HEX_ARRAY[byteHex & 0x0F];
+ }
+ return new String(chars);
+ }
+
+ private static File getEphemeralApplicationsDir(int userId) {
+ return new File(Environment.getUserSystemDirectory(userId),
+ EPHEMERAL_APPS_FOLDER);
+ }
+
+ private static File getEphemeralApplicationDir(String packageName, int userId) {
+ return new File (getEphemeralApplicationsDir(userId), packageName);
+ }
+
+ private static void deleteDir(File dir) {
+ File[] files = dir.listFiles();
+ if (files != null) {
+ for (File file : dir.listFiles()) {
+ deleteDir(file);
+ }
+ }
+ dir.delete();
+ }
+
+ private static final class UninstalledEphemeralAppState {
+ final EphemeralApplicationInfo mEphemeralApplicationInfo;
+ final long mTimestamp;
+
+ public UninstalledEphemeralAppState(EphemeralApplicationInfo ephemeralApp,
+ long timestamp) {
+ mEphemeralApplicationInfo = ephemeralApp;
+ mTimestamp = timestamp;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d94e5f4..2f8157e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -105,6 +105,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.AppsQueryHelper;
+import android.content.pm.EphemeralApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IOnPermissionsChangeListener;
import android.content.pm.IPackageDataObserver;
@@ -144,6 +145,7 @@
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Debug;
@@ -200,6 +202,7 @@
import android.util.Xml;
import android.view.Display;
+import com.android.internal.annotations.GuardedBy;
import dalvik.system.DexFile;
import dalvik.system.VMRuntime;
@@ -207,7 +210,6 @@
import libcore.util.EmptyArray;
import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.EphemeralResolveInfo;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
@@ -512,6 +514,8 @@
// If a recursive restorecon of /data/data/<pkg> is needed.
private boolean mShouldRestoreconData = SELinuxMMAC.shouldRestorecon();
+ private final EphemeralApplicationRegistry mEphemeralApplicationRegistry;
+
public static final class SharedLibraryEntry {
public final String path;
public final String apk;
@@ -1396,6 +1400,10 @@
args.installGrantPermissions);
}
+ synchronized (mPackages) {
+ mEphemeralApplicationRegistry.onPackageInstalledLPw(res.pkg);
+ }
+
// Determine the set of users who are adding this
// package for the first time vs. those who are seeing
// an update.
@@ -2405,6 +2413,8 @@
mEphemeralInstallerComponent = null;
mEphemeralResolverConnection = null;
}
+
+ mEphemeralApplicationRegistry = new EphemeralApplicationRegistry(this);
} // synchronized (mPackages)
} // synchronized (mInstallLock)
@@ -5784,6 +5794,82 @@
}
}
+ @Override
+ public ParceledListSlice<EphemeralApplicationInfo> getEphemeralApplications(int userId) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_EPHEMERAL_APPS,
+ "getEphemeralApplications");
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+ "getEphemeralApplications");
+ synchronized (mPackages) {
+ List<EphemeralApplicationInfo> ephemeralApps = mEphemeralApplicationRegistry
+ .getEphemeralApplicationsLPw(userId);
+ if (ephemeralApps != null) {
+ return new ParceledListSlice<>(ephemeralApps);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isEphemeralApplication(String packageName, int userId) {
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+ "isEphemeral");
+ if (!isCallerSameApp(packageName)) {
+ return false;
+ }
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(packageName);
+ if (pkg != null) {
+ return pkg.applicationInfo.isEphemeralApp();
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public byte[] getEphemeralApplicationCookie(String packageName, int userId) {
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+ "getCookie");
+ if (!isCallerSameApp(packageName)) {
+ return null;
+ }
+ synchronized (mPackages) {
+ return mEphemeralApplicationRegistry.getEphemeralApplicationCookieLPw(
+ packageName, userId);
+ }
+ }
+
+ @Override
+ public boolean setEphemeralApplicationCookie(String packageName, byte[] cookie, int userId) {
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+ "setCookie");
+ if (!isCallerSameApp(packageName)) {
+ return false;
+ }
+ synchronized (mPackages) {
+ return mEphemeralApplicationRegistry.setEphemeralApplicationCookieLPw(
+ packageName, cookie, userId);
+ }
+ }
+
+ @Override
+ public Bitmap getEphemeralApplicationIcon(String packageName, int userId) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_EPHEMERAL_APPS,
+ "getEphemeralApplicationIcon");
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+ "getEphemeralApplicationIcon");
+ synchronized (mPackages) {
+ return mEphemeralApplicationRegistry.getEphemeralApplicationIconLPw(
+ packageName, userId);
+ }
+ }
+
+ private boolean isCallerSameApp(String packageName) {
+ PackageParser.Package pkg = mPackages.get(packageName);
+ return pkg != null
+ && UserHandle.getAppId(Binder.getCallingUid()) == pkg.applicationInfo.uid;
+ }
+
public List<ApplicationInfo> getPersistentApplications(int flags) {
final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
@@ -12270,8 +12356,7 @@
// First find the old package info and check signatures
synchronized(mPackages) {
oldPackage = mPackages.get(pkgName);
- final boolean oldIsEphemeral
- = ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_EPHEMERAL) != 0);
+ final boolean oldIsEphemeral = oldPackage.applicationInfo.isEphemeralApp();
if (isEphemeral && !oldIsEphemeral) {
// can't downgrade from full to ephemeral
Slog.w(TAG, "Can't replace app with ephemeral: " + pkgName);
@@ -13090,11 +13175,11 @@
}
private static boolean isEphemeral(PackageParser.Package pkg) {
- return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EPHEMERAL) != 0;
+ return pkg.applicationInfo.isEphemeralApp();
}
private static boolean isEphemeral(PackageSetting ps) {
- return (ps.pkgFlags & ApplicationInfo.FLAG_EPHEMERAL) != 0;
+ return ps.pkg != null && isEphemeral(ps.pkg);
}
private static boolean isSystemApp(PackageParser.Package pkg) {
@@ -13295,11 +13380,14 @@
boolean removedForAllUsers = false;
boolean systemUpdate = false;
+ PackageParser.Package uninstalledPkg;
+
// for the uninstall-updates case and restricted profiles, remember the per-
// userhandle installed state
int[] allUsers;
boolean[] perUserInstalled;
synchronized (mPackages) {
+ uninstalledPkg = mPackages.get(packageName);
PackageSetting ps = mSettings.mPackages.get(packageName);
allUsers = sUserManager.getUserIds();
perUserInstalled = new boolean[allUsers.length];
@@ -13314,8 +13402,13 @@
true, allUsers, perUserInstalled,
flags | REMOVE_CHATTY, info, true);
systemUpdate = info.isRemovedPackageSystemUpdate;
- if (res && !systemUpdate && mPackages.get(packageName) == null) {
- removedForAllUsers = true;
+ synchronized (mPackages) {
+ if (res) {
+ if (!systemUpdate && mPackages.get(packageName) == null) {
+ removedForAllUsers = true;
+ }
+ mEphemeralApplicationRegistry.onPackageUninstalledLPw(uninstalledPkg);
+ }
}
if (DEBUG_REMOVE) Slog.d(TAG, "delete res: systemUpdate=" + systemUpdate
+ " removedForAllUsers=" + removedForAllUsers);
@@ -16450,7 +16543,7 @@
if (userDir.exists()) continue;
try {
- sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber);
+ sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, user.isEphemeral());
UserManagerService.enforceSerialNumber(userDir, user.serialNumber);
} catch (IOException e) {
Log.wtf(TAG, "Failed to create user directory on " + volumeUuid, e);
@@ -16837,6 +16930,7 @@
mUserNeedsBadging.delete(userHandle);
mSettings.removeUserLPw(userHandle);
mPendingBroadcasts.remove(userHandle);
+ mEphemeralApplicationRegistry.onUserRemovedLPw(userHandle);
}
synchronized (mInstallLock) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5910c10..0b59c16 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
@@ -58,6 +59,7 @@
import android.system.OsConstants;
import android.util.AtomicFile;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -90,6 +92,7 @@
import java.util.List;
import libcore.io.IoUtils;
+import libcore.util.Objects;
/**
* Service for {@link UserManager}.
@@ -107,6 +110,7 @@
private static final boolean DBG_WITH_STACKTRACE = false; // DO NOT SUBMIT WITH TRUE
private static final String TAG_NAME = "name";
+ private static final String TAG_ACCOUNT = "account";
private static final String ATTR_FLAGS = "flags";
private static final String ATTR_ICON_PATH = "icon";
private static final String ATTR_ID = "id";
@@ -178,6 +182,14 @@
private final SparseArray<UserInfo> mUsers = new SparseArray<>();
/**
+ * This collection contains each user's account name if the user chose to set one up
+ * during the initial user creation process. Keeping this information separate from mUsers
+ * to avoid accidentally leak it.
+ */
+ @GuardedBy("mUsersLock")
+ private final SparseArray<String> mUserAccounts = new SparseArray<>();
+
+ /**
* User restrictions set via UserManager. This doesn't include restrictions set by
* device owner / profile owners.
*
@@ -306,13 +318,13 @@
}
void systemReady() {
- // Prune out any partially created/partially removed users.
+ // Prune out any partially created, partially removed and ephemeral users.
ArrayList<UserInfo> partials = new ArrayList<>();
synchronized (mUsersLock) {
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
UserInfo ui = mUsers.valueAt(i);
- if ((ui.partial || ui.guestToRemove) && i != 0) {
+ if ((ui.partial || ui.guestToRemove || ui.isEphemeral()) && i != 0) {
partials.add(ui);
}
}
@@ -336,6 +348,33 @@
}
@Override
+ public String getUserAccount(int userId) {
+ checkManageUserAndAcrossUsersFullPermission("get user account");
+ synchronized (mUsersLock) {
+ return mUserAccounts.get(userId);
+ }
+ }
+
+ @Override
+ public void setUserAccount(int userId, String accountName) {
+ checkManageUserAndAcrossUsersFullPermission("set user account");
+ UserInfo userToUpdate = null;
+ synchronized (mPackagesLock) {
+ synchronized (mUsersLock) {
+ String currentAccount = mUserAccounts.get(userId);
+ if (!Objects.equal(currentAccount, accountName)) {
+ mUserAccounts.put(userId, accountName);
+ userToUpdate = mUsers.get(userId);
+ }
+ }
+
+ if (userToUpdate != null) {
+ writeUserLP(userToUpdate);
+ }
+ }
+ }
+
+ @Override
public UserInfo getPrimaryUser() {
checkManageUsersPermission("query users");
synchronized (mUsersLock) {
@@ -845,6 +884,15 @@
}
@Override
+ public boolean hasBaseUserRestriction(String restrictionKey, int userId) {
+ checkManageUsersPermission("hasBaseUserRestriction");
+ synchronized (mRestrictionsLock) {
+ Bundle bundle = mBaseUserRestrictions.get(userId);
+ return (bundle != null && bundle.getBoolean(restrictionKey, false));
+ }
+ }
+
+ @Override
public void setUserRestriction(String key, boolean value, int userId) {
checkManageUsersPermission("setUserRestriction");
synchronized (mRestrictionsLock) {
@@ -1043,6 +1091,30 @@
/**
* Enforces that only the system UID or root's UID or apps that have the
+ * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} and
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL INTERACT_ACROSS_USERS_FULL}
+ * permissions can make certain calls to the UserManager.
+ *
+ * @param message used as message if SecurityException is thrown
+ * @throws SecurityException if the caller does not have enough privilege.
+ */
+ private static final void checkManageUserAndAcrossUsersFullPermission(String message) {
+ final int uid = Binder.getCallingUid();
+ if (uid != Process.SYSTEM_UID && uid != 0
+ && ActivityManager.checkComponentPermission(
+ Manifest.permission.MANAGE_USERS,
+ uid, -1, true) != PackageManager.PERMISSION_GRANTED
+ && ActivityManager.checkComponentPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "You need MANAGE_USERS and INTERACT_ACROSS_USERS_FULL permission to: "
+ + message);
+ }
+ }
+
+ /**
+ * Enforces that only the system UID or root's UID or apps that have the
* {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS}
* permission can make certain calls to the UserManager.
*
@@ -1059,13 +1131,6 @@
}
}
- private static void checkSystemOrRoot(String message) {
- final int uid = Binder.getCallingUid();
- if (uid != Process.SYSTEM_UID && uid != 0) {
- throw new SecurityException("Only system may call: " + message);
- }
- }
-
private void writeBitmapLP(UserInfo info, Bitmap bitmap) {
try {
File dir = new File(mUsersDir, Integer.toString(info.id));
@@ -1147,11 +1212,14 @@
final String name = parser.getName();
if (name.equals(TAG_USER)) {
String id = parser.getAttributeValue(null, ATTR_ID);
- UserInfo user = readUserLP(Integer.parseInt(id));
+ Pair<UserInfo, String> userPair = readUserLP(Integer.parseInt(id));
- if (user != null) {
+ if (userPair != null) {
+ UserInfo user = userPair.first;
+ String account = userPair.second;
synchronized (mUsersLock) {
mUsers.put(user.id, user);
+ mUserAccounts.put(user.id, account);
if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) {
mNextSerialNumber = user.id + 1;
}
@@ -1351,6 +1419,17 @@
mDevicePolicyLocalUserRestrictions.get(userInfo.id),
TAG_DEVICE_POLICY_RESTRICTIONS);
}
+ // Update the account field if it is set.
+ String account;
+ synchronized (mUsersLock) {
+ account = mUserAccounts.get(userInfo.id);
+ }
+ if (account != null) {
+ serializer.startTag(null, TAG_ACCOUNT);
+ serializer.text(account);
+ serializer.endTag(null, TAG_ACCOUNT);
+ }
+
serializer.endTag(null, TAG_USER);
serializer.endDocument();
@@ -1423,10 +1502,11 @@
}
}
- private UserInfo readUserLP(int id) {
+ private Pair<UserInfo, String> readUserLP(int id) {
int flags = 0;
int serialNumber = id;
String name = null;
+ String account = null;
String iconPath = null;
long creationTime = 0L;
long lastLoggedInTime = 0L;
@@ -1495,6 +1575,11 @@
UserRestrictionsUtils.readRestrictions(parser, baseRestrictions);
} else if (TAG_DEVICE_POLICY_RESTRICTIONS.equals(tag)) {
UserRestrictionsUtils.readRestrictions(parser, localRestrictions);
+ } else if (TAG_ACCOUNT.equals(tag)) {
+ type = parser.next();
+ if (type == XmlPullParser.TEXT) {
+ account = parser.getText();
+ }
}
}
}
@@ -1511,7 +1596,7 @@
mBaseUserRestrictions.put(id, baseRestrictions);
mDevicePolicyLocalUserRestrictions.put(id, localRestrictions);
}
- return userInfo;
+ return new Pair<>(userInfo, account);
} catch (IOException ioe) {
} catch (XmlPullParserException pe) {
@@ -1666,6 +1751,10 @@
}
}
}
+
+ if (parent != null && parent.isEphemeral()) {
+ flags |= UserInfo.FLAG_EPHEMERAL;
+ }
userId = getNextAvailableId();
userInfo = new UserInfo(userId, name, null, flags);
userInfo.serialNumber = mNextSerialNumber++;
@@ -1694,12 +1783,13 @@
}
}
final StorageManager storage = mContext.getSystemService(StorageManager.class);
- storage.createUserKey(userId, userInfo.serialNumber);
+ storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
final String volumeUuid = vol.getFsUuid();
try {
final File userDir = Environment.getDataUserDirectory(volumeUuid, userId);
- storage.prepareUserStorage(volumeUuid, userId, userInfo.serialNumber);
+ storage.prepareUserStorage(
+ volumeUuid, userId, userInfo.serialNumber, userInfo.isEphemeral());
enforceSerialNumber(userDir, userInfo.serialNumber);
} catch (IOException e) {
Log.wtf(LOG_TAG, "Failed to create user directory on " + volumeUuid, e);
@@ -1933,6 +2023,7 @@
// Remove this user from the list
synchronized (mUsersLock) {
mUsers.remove(userHandle);
+ mUserAccounts.delete(userHandle);
mIsUserManaged.delete(userHandle);
}
synchronized (mRestrictionsLock) {
@@ -2493,6 +2584,11 @@
pw, " ", mCachedEffectiveUserRestrictions.get(user.id));
}
pw.println();
+ String accountName = mUserAccounts.get(userId);
+ if (accountName != null) {
+ pw.print(" Account name: " + accountName);
+ pw.println();
+ }
}
}
pw.println(" Device policy global restrictions:");
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index fe427d3..b9a9d6e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -396,6 +396,7 @@
boolean mDeskDockEnablesAccelerometer;
int mLidKeyboardAccessibility;
int mLidNavigationAccessibility;
+ boolean mLidControlsScreenLock;
boolean mLidControlsSleep;
int mShortPressOnPowerBehavior;
int mLongPressOnPowerBehavior;
@@ -1440,6 +1441,8 @@
com.android.internal.R.integer.config_lidKeyboardAccessibility);
mLidNavigationAccessibility = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lidNavigationAccessibility);
+ mLidControlsScreenLock = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_lidControlsScreenLock);
mLidControlsSleep = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_lidControlsSleep);
mTranslucentDecorEnabled = mContext.getResources().getBoolean(
@@ -6378,6 +6381,8 @@
mPowerManager.goToSleep(SystemClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+ } else if (mLidState == LID_CLOSED && mLidControlsScreenLock) {
+ mWindowManagerFuncs.lockDeviceNow();
}
synchronized (mLock) {
@@ -6970,6 +6975,7 @@
pw.print(prefix); pw.print("mLidKeyboardAccessibility=");
pw.print(mLidKeyboardAccessibility);
pw.print(" mLidNavigationAccessibility="); pw.print(mLidNavigationAccessibility);
+ pw.print(" mLidControlsScreenLock="); pw.println(mLidControlsScreenLock);
pw.print(" mLidControlsSleep="); pw.println(mLidControlsSleep);
pw.print(prefix);
pw.print("mShortPressOnPowerBehavior="); pw.print(mShortPressOnPowerBehavior);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 334a4dcc..dbbbb58 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5252,6 +5252,12 @@
// Called by window manager policy. Not exposed externally.
@Override
+ public void lockDeviceNow() {
+ lockNow(null);
+ }
+
+ // Called by window manager policy. Not exposed externally.
+ @Override
public int getCameraLensCoverState() {
int sw = mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY,
InputManagerService.SW_CAMERA_LENS_COVER);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index eed5419..5cf8ac0e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3541,6 +3541,18 @@
}
}
+ private void enforceCanManageInstalledKeys(ComponentName who) {
+ if (who == null) {
+ if (!isCallerDelegatedCertInstaller()) {
+ throw new SecurityException("who == null, but caller is not cert installer");
+ }
+ } else {
+ synchronized (this) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ }
+ }
+ }
+
private boolean isCallerDelegatedCertInstaller() {
final int callingUid = mInjector.binderGetCallingUid();
final int userHandle = UserHandle.getUserId(callingUid);
@@ -3630,27 +3642,20 @@
@Override
public boolean installKeyPair(ComponentName who, byte[] privKey, byte[] cert, String alias) {
- if (who == null) {
- if (!isCallerDelegatedCertInstaller()) {
- throw new SecurityException("who == null, but caller is not cert installer");
- }
- } else {
- synchronized (this) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- }
- }
+ enforceCanManageInstalledKeys(who);
+
final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
final long id = mInjector.binderClearCallingIdentity();
try {
- final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
- try {
- IKeyChainService keyChain = keyChainConnection.getService();
- return keyChain.installKeyPair(privKey, cert, alias);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "Installing certificate", e);
- } finally {
- keyChainConnection.close();
- }
+ final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
+ try {
+ IKeyChainService keyChain = keyChainConnection.getService();
+ return keyChain.installKeyPair(privKey, cert, alias);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Installing certificate", e);
+ } finally {
+ keyChainConnection.close();
+ }
} catch (InterruptedException e) {
Log.w(LOG_TAG, "Interrupted while installing certificate", e);
Thread.currentThread().interrupt();
@@ -3661,6 +3666,31 @@
}
@Override
+ public boolean removeKeyPair(ComponentName who, String alias) {
+ enforceCanManageInstalledKeys(who);
+
+ final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+ final long id = Binder.clearCallingIdentity();
+ try {
+ final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
+ try {
+ IKeyChainService keyChain = keyChainConnection.getService();
+ return keyChain.removeKeyPair(alias);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Removing keypair", e);
+ } finally {
+ keyChainConnection.close();
+ }
+ } catch (InterruptedException e) {
+ Log.w(LOG_TAG, "Interrupted while removing keypair", e);
+ Thread.currentThread().interrupt();
+ } finally {
+ Binder.restoreCallingIdentity(id);
+ }
+ return false;
+ }
+
+ @Override
public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias,
final IBinder response) {
// Caller UID needs to be trusted, so we restrict this method to SYSTEM_UID callers.
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 9e70138..48a11c5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -16,9 +16,12 @@
package com.android.server.pm;
+import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.FileUtils;
import android.os.Parcelable;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.test.AndroidTestCase;
import android.util.AtomicFile;
@@ -29,6 +32,7 @@
public class UserManagerServiceTest extends AndroidTestCase {
private static String[] STRING_ARRAY = new String[] {"<tag", "<![CDATA["};
private File restrictionsFile;
+ private int tempUserId = UserHandle.USER_NULL;
@Override
protected void setUp() throws Exception {
@@ -40,6 +44,9 @@
@Override
protected void tearDown() throws Exception {
restrictionsFile.delete();
+ if (tempUserId != UserHandle.USER_NULL) {
+ UserManager.get(mContext).removeUser(tempUserId);
+ }
super.tearDown();
}
@@ -55,6 +62,16 @@
assertBundle(bundle);
}
+ public void testAddUserWithAccount() {
+ UserManager um = UserManager.get(mContext);
+ UserInfo user = um.createUser("Test User", 0);
+ assertNotNull(user);
+ tempUserId = user.id;
+ String accountName = "Test Account";
+ um.setUserAccount(tempUserId, accountName);
+ assertEquals(accountName, um.getUserAccount(tempUserId));
+ }
+
private Bundle createBundle() {
Bundle result = new Bundle();
// Tests for 6 allowed types: Integer, Boolean, String, String[], Bundle and Parcelable[]
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index b5b4e5f..9eb1665 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -247,6 +247,14 @@
"android.telecom.extra.CONNECTION_SERVICE";
/**
+ * Optional extra for communicating the call technology used by a
+ * {@link com.android.internal.telephony.Connection} to Telecom
+ * @hide
+ */
+ public static final String EXTRA_CALL_TECHNOLOGY_TYPE =
+ "android.telecom.extra.CALL_TECHNOLOGY_TYPE";
+
+ /**
* An optional {@link android.content.Intent#ACTION_CALL} intent extra denoting the
* package name of the app specifying an alternative gateway for the call.
* The value is a string.
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 225edf8..4c3b598 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -181,18 +181,6 @@
}
@Override
- public File getDeviceEncryptedFilesDir() {
- throw new UnsupportedOperationException();
- }
-
- /** {@hide} */
- @SystemApi
- @Override
- public File getCredentialEncryptedFilesDir() {
- throw new UnsupportedOperationException();
- }
-
- @Override
public File getNoBackupFilesDir() {
throw new UnsupportedOperationException();
}
@@ -705,4 +693,24 @@
public File[] getExternalMediaDirs() {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public Context createDeviceEncryptedContext(Context context) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Context createCredentialEncryptedContext(Context context) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isDeviceEncrypted() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isCredentialEncrypted() {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 17c24af..f7c63d1 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -16,6 +16,7 @@
package android.test.mock;
+import android.annotation.NonNull;
import android.app.PackageInstallObserver;
import android.content.ComponentName;
import android.content.Intent;
@@ -24,6 +25,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ContainerEncryptionParams;
+import android.content.pm.EphemeralApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
@@ -286,6 +288,38 @@
throw new UnsupportedOperationException();
}
+ /** @hide */
+ @Override
+ public List<EphemeralApplicationInfo> getEphemeralApplications() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** @hide */
+ @Override
+ public Drawable getEphemeralApplicationIcon(String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public byte[] getEphemeralCookie() {
+ return new byte[0];
+ }
+
+ @Override
+ public boolean isEphemeralApplication() {
+ return false;
+ }
+
+ @Override
+ public int getEphemeralCookieMaxSizeBytes() {
+ return 0;
+ }
+
+ @Override
+ public boolean setEphemeralCookie(@NonNull byte[] cookie) {
+ return false;
+ }
+
@Override
public ResolveInfo resolveActivity(Intent intent, int flags) {
throw new UnsupportedOperationException();
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
index f42e6b7..46ee914 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -23,7 +23,7 @@
#include "flatten/TableFlattener.h"
#include "util/BigBuffer.h"
-#include <base/macros.h>
+#include <android-base/macros.h>
#include <type_traits>
#include <numeric>
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 5cc7aa7..46b5205 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -27,7 +27,7 @@
#include <androidfw/ResourceTypes.h>
#include <androidfw/TypeWrappers.h>
-#include <base/macros.h>
+#include <android-base/macros.h>
#include <map>
#include <string>
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 9214759..bd5335e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1377,18 +1377,6 @@
}
@Override
- public File getDeviceEncryptedFilesDir() {
- // pass
- return null;
- }
-
- @Override
- public File getCredentialEncryptedFilesDir() {
- // pass
- return null;
- }
-
- @Override
public File getNoBackupFilesDir() {
// pass
return null;
@@ -1814,4 +1802,26 @@
Integer pos = mScrollYPos.get(view);
return pos != null ? pos : 0;
}
+
+ @Override
+ public Context createDeviceEncryptedContext(Context context) {
+ // pass
+ return null;
+ }
+
+ @Override
+ public Context createCredentialEncryptedContext(Context context) {
+ // pass
+ return null;
+ }
+
+ @Override
+ public boolean isDeviceEncrypted() {
+ return false;
+ }
+
+ @Override
+ public boolean isCredentialEncrypted() {
+ return false;
+ }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index e3a19e7..bab25c0 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -16,6 +16,7 @@
package com.android.layoutlib.bridge.android;
+import android.annotation.NonNull;
import android.app.PackageInstallObserver;
import android.content.ComponentName;
import android.content.Intent;
@@ -24,6 +25,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ContainerEncryptionParams;
+import android.content.pm.EphemeralApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
@@ -251,6 +253,36 @@
}
@Override
+ public List<EphemeralApplicationInfo> getEphemeralApplications() {
+ return null;
+ }
+
+ @Override
+ public Drawable getEphemeralApplicationIcon(String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public byte[] getEphemeralCookie() {
+ return new byte[0];
+ }
+
+ @Override
+ public boolean isEphemeralApplication() {
+ return false;
+ }
+
+ @Override
+ public int getEphemeralCookieMaxSizeBytes() {
+ return 0;
+ }
+
+ @Override
+ public boolean setEphemeralCookie(@NonNull byte[] cookie) {
+ return false;
+ }
+
+ @Override
public String[] getSystemSharedLibraryNames() {
return new String[0];
}