diff options
131 files changed, 2740 insertions, 982 deletions
diff --git a/api/current.txt b/api/current.txt index 117cafb..009b534 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1759,6 +1759,7 @@ package android { field public static final int list = 16908298; // 0x102000a field public static final int mask = 16908353; // 0x1020041 field public static final int message = 16908299; // 0x102000b + field public static final int navigationBarBackground = 16908355; // 0x1020043 field public static final int paste = 16908322; // 0x1020022 field public static final int primary = 16908300; // 0x102000c field public static final int progress = 16908301; // 0x102000d @@ -1767,6 +1768,7 @@ package android { field public static final int selectTextMode = 16908333; // 0x102002d field public static final int selectedIcon = 16908302; // 0x102000e field public static final int startSelectingText = 16908328; // 0x1020028 + field public static final int statusBarBackground = 16908354; // 0x1020042 field public static final int stopSelectingText = 16908329; // 0x1020029 field public static final int summary = 16908304; // 0x1020010 field public static final int switchInputMethod = 16908324; // 0x1020024 @@ -5748,7 +5750,7 @@ package android.app.usage { } public final class UsageStatsManager { - method public android.util.ArrayMap<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long); + method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long); method public android.app.usage.UsageEvents queryEvents(long, long); method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long); field public static final int INTERVAL_BEST = 4; // 0x4 @@ -7287,8 +7289,8 @@ package android.content { method public void registerComponentCallbacks(android.content.ComponentCallbacks); method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter); method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler); - method public abstract void removeStickyBroadcast(android.content.Intent); - method public abstract void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); + method public abstract deprecated void removeStickyBroadcast(android.content.Intent); + method public abstract deprecated void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public abstract void revokeUriPermission(android.net.Uri, int); method public abstract void sendBroadcast(android.content.Intent); method public abstract void sendBroadcast(android.content.Intent, java.lang.String); @@ -7297,10 +7299,10 @@ package android.content { method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String); method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); method public abstract void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); - method public abstract void sendStickyBroadcast(android.content.Intent); - method public abstract void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); - method public abstract void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); - method public abstract void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); + method public abstract deprecated void sendStickyBroadcast(android.content.Intent); + method public abstract deprecated void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); + method public abstract deprecated void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); + method public abstract deprecated void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); method public abstract void setTheme(int); method public abstract deprecated void setWallpaper(android.graphics.Bitmap) throws java.io.IOException; method public abstract deprecated void setWallpaper(java.io.InputStream) throws java.io.IOException; @@ -8580,9 +8582,6 @@ package android.content.pm { field public java.lang.String targetPackage; } - public class KeySet { - } - public class LabeledIntent extends android.content.Intent { ctor public LabeledIntent(android.content.Intent, java.lang.String, int, int); ctor public LabeledIntent(android.content.Intent, java.lang.String, java.lang.CharSequence, int); @@ -8818,7 +8817,6 @@ package android.content.pm { method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int); method public abstract java.lang.String getInstallerPackageName(java.lang.String); method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.content.pm.KeySet getKeySetByAlias(java.lang.String, java.lang.String); method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String); method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String); method public abstract java.lang.String getNameForUid(int); @@ -8838,15 +8836,12 @@ package android.content.pm { method public abstract android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.content.pm.KeySet getSigningKeySet(java.lang.String); method public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures(); method public abstract java.lang.String[] getSystemSharedLibraryNames(); method public abstract java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo); 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 isSafeMode(); - method public abstract boolean isSignedBy(java.lang.String, android.content.pm.KeySet); - method public abstract boolean isSignedByExactly(java.lang.String, android.content.pm.KeySet); method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int); method public abstract java.util.List<android.content.pm.ProviderInfo> queryContentProviders(java.lang.String, int, int); method public abstract java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int); @@ -17199,10 +17194,6 @@ package android.net { field public static final android.os.Parcelable.Creator CREATOR; } - public abstract interface NetworkBoundURLFactory { - method public abstract java.net.URL getBoundURL(android.net.Network, java.net.URL) throws java.net.MalformedURLException; - } - public final class NetworkCapabilities implements android.os.Parcelable { ctor public NetworkCapabilities(android.net.NetworkCapabilities); method public int describeContents(); @@ -28410,6 +28401,7 @@ package android.telecomm { } public static class PhoneAccount.Builder { + ctor public PhoneAccount.Builder(); method public android.telecomm.PhoneAccount build(); method public android.telecomm.PhoneAccount.Builder withAccountHandle(android.telecomm.PhoneAccountHandle); method public android.telecomm.PhoneAccount.Builder withCapabilities(int); @@ -29790,7 +29782,6 @@ package android.test.mock { method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int); method public java.lang.String getInstallerPackageName(java.lang.String); method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; - method public android.content.pm.KeySet getKeySetByAlias(java.lang.String, java.lang.String); method public android.content.Intent getLaunchIntentForPackage(java.lang.String); method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String); method public java.lang.String getNameForUid(int); @@ -29809,15 +29800,12 @@ package android.test.mock { method public android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo); method public android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; - method public android.content.pm.KeySet getSigningKeySet(java.lang.String); method public android.content.pm.FeatureInfo[] getSystemAvailableFeatures(); method public java.lang.String[] getSystemSharedLibraryNames(); method public java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo); 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 isSafeMode(); - method public boolean isSignedBy(java.lang.String, android.content.pm.KeySet); - method public boolean isSignedByExactly(java.lang.String, android.content.pm.KeySet); method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int); method public java.util.List<android.content.pm.ProviderInfo> queryContentProviders(java.lang.String, int, int); method public java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int); @@ -39575,22 +39563,6 @@ package android.widget { } -package com.android.internal.telecomm { - - public abstract interface RemoteServiceCallback implements android.os.IInterface { - method public abstract void onError() throws android.os.RemoteException; - method public abstract void onResult(java.util.List<android.content.ComponentName>, java.util.List<android.os.IBinder>) throws android.os.RemoteException; - } - - public static abstract class RemoteServiceCallback.Stub extends android.os.Binder implements com.android.internal.telecomm.RemoteServiceCallback { - ctor public RemoteServiceCallback.Stub(); - method public android.os.IBinder asBinder(); - method public static com.android.internal.telecomm.RemoteServiceCallback asInterface(android.os.IBinder); - method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException; - } - -} - package com.android.internal.util { public abstract interface Predicate { diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index d9b40b1..da34094 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -866,6 +866,7 @@ public final class Pm { private void runInstall() { int installFlags = PackageManager.INSTALL_ALL_USERS; + int userId = UserHandle.USER_ALL; String installerPackageName = null; String opt; @@ -909,6 +910,13 @@ public final class Pm { } } else if (opt.equals("--abi")) { abi = checkAbiArgument(nextOptionData()); + } else if (opt.equals("--user")) { + userId = Integer.parseInt(nextOptionData()); + if (userId == UserHandle.USER_ALL) { + installFlags |= PackageManager.INSTALL_ALL_USERS; + } else { + installFlags &= ~PackageManager.INSTALL_ALL_USERS; + } } else { System.err.println("Error: Unknown option: " + opt); return; @@ -953,8 +961,8 @@ public final class Pm { VerificationParams verificationParams = new VerificationParams(verificationURI, originatingURI, referrerURI, VerificationParams.NO_UID, null); - mPm.installPackage(apkFilePath, obs.getBinder(), installFlags, installerPackageName, - verificationParams, abi); + mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags, installerPackageName, + verificationParams, abi, userId); synchronized (obs) { while (!obs.finished) { diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 9342ae5..6843827 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1494,57 +1494,52 @@ final class ApplicationPackageManager extends PackageManager { return false; } + /** @hide */ @Override public KeySet getKeySetByAlias(String packageName, String alias) { Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(alias); - IBinder keySetToken; + KeySet ks; try { - keySetToken = mPM.getKeySetByAlias(packageName, alias); + ks = mPM.getKeySetByAlias(packageName, alias); } catch (RemoteException e) { return null; } - if (keySetToken == null) { - return null; - } - return new KeySet(keySetToken); + return ks; } + /** @hide */ @Override public KeySet getSigningKeySet(String packageName) { Preconditions.checkNotNull(packageName); - IBinder keySetToken; + KeySet ks; try { - keySetToken = mPM.getSigningKeySet(packageName); + ks = mPM.getSigningKeySet(packageName); } catch (RemoteException e) { return null; } - if (keySetToken == null) { - return null; - } - return new KeySet(keySetToken); + return ks; } - + /** @hide */ @Override public boolean isSignedBy(String packageName, KeySet ks) { Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(ks); - IBinder keySetToken = ks.getToken(); try { - return mPM.isPackageSignedByKeySet(packageName, keySetToken); + return mPM.isPackageSignedByKeySet(packageName, ks); } catch (RemoteException e) { return false; } } + /** @hide */ @Override public boolean isSignedByExactly(String packageName, KeySet ks) { Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(ks); - IBinder keySetToken = ks.getToken(); try { - return mPM.isPackageSignedByKeySetExactly(packageName, keySetToken); + return mPM.isPackageSignedByKeySetExactly(packageName, ks); } catch (RemoteException e) { return false; } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 214f50c..dbd180f 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -23,6 +23,7 @@ import android.content.ComponentName; import android.content.Intent; import android.content.pm.ParceledListSlice; import android.net.Uri; +import android.os.Bundle; import android.service.notification.Condition; import android.service.notification.IConditionListener; import android.service.notification.IConditionProvider; @@ -58,13 +59,15 @@ interface INotificationManager void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id); void cancelNotificationsFromListener(in INotificationListener token, in String[] keys); - ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys); + ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys, int trim); void requestHintsFromListener(in INotificationListener token, int hints); int getHintsFromListener(in INotificationListener token); void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter); int getInterruptionFilterFromListener(in INotificationListener token); + void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim); ComponentName getEffectsSuppressor(); + boolean matchesCallFilter(in Bundle extras); ZenModeConfig getZenModeConfig(); boolean setZenModeConfig(in ZenModeConfig config); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 8a26ba5..966d2ce 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1424,6 +1424,8 @@ public class Notification implements Parcelable extras.remove(Notification.EXTRA_LARGE_ICON); extras.remove(Notification.EXTRA_LARGE_ICON_BIG); extras.remove(Notification.EXTRA_PICTURE); + // Prevent light notifications from being rebuilt. + extras.remove(Builder.EXTRA_NEEDS_REBUILD); } } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index fc047de..7dc1ad6 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -20,6 +20,7 @@ import android.annotation.SdkConstant; import android.app.Notification.Builder; import android.content.ComponentName; import android.content.Context; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; @@ -251,5 +252,17 @@ public class NotificationManager } } + /** + * @hide + */ + public boolean matchesCallFilter(Bundle extras) { + INotificationManager service = getService(); + try { + return service.matchesCallFilter(extras); + } catch (RemoteException e) { + return false; + } + } + private Context mContext; } diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 2431ad0..fb80de2 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -106,7 +106,9 @@ public final class UsageEvents implements Parcelable { } /** - * The time at which this event occurred. + * The time at which this event occurred, measured in milliseconds since the epoch. + * <p/> + * See {@link System#currentTimeMillis()}. */ public long getTimeStamp() { return mTimeStamp; diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java index e47a802..abfc435 100644 --- a/core/java/android/app/usage/UsageStats.java +++ b/core/java/android/app/usage/UsageStats.java @@ -81,28 +81,36 @@ public final class UsageStats implements Parcelable { } /** - * Get the beginning of the time range this {@link android.app.usage.UsageStats} represents. + * Get the beginning of the time range this {@link android.app.usage.UsageStats} represents, + * measured in milliseconds since the epoch. + * <p/> + * See {@link System#currentTimeMillis()}. */ public long getFirstTimeStamp() { return mBeginTimeStamp; } /** - * Get the end of the time range this {@link android.app.usage.UsageStats} represents. + * Get the end of the time range this {@link android.app.usage.UsageStats} represents, + * measured in milliseconds since the epoch. + * <p/> + * See {@link System#currentTimeMillis()}. */ public long getLastTimeStamp() { return mEndTimeStamp; } /** - * Get the last time this package was used. + * Get the last time this package was used, measured in milliseconds since the epoch. + * <p/> + * See {@link System#currentTimeMillis()}. */ public long getLastTimeUsed() { return mLastTimeUsed; } /** - * Get the total time this package spent in the foreground. + * Get the total time this package spent in the foreground, measured in milliseconds. */ public long getTotalTimeInForeground() { return mTotalTimeInForeground; diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index f9b8928..5830fcf 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -23,6 +23,7 @@ import android.util.ArrayMap; import java.util.Collections; import java.util.List; +import java.util.Map; /** * Provides access to device usage history and statistics. Usage data is aggregated into @@ -149,7 +150,6 @@ public final class UsageStatsManager { * @param endTime The exclusive end of the range of events to include in the results. * @return A {@link UsageEvents}. */ - @SuppressWarnings("unchecked") public UsageEvents queryEvents(long beginTime, long endTime) { try { UsageEvents iter = mService.queryEvents(beginTime, endTime, @@ -170,15 +170,13 @@ public final class UsageStatsManager { * * @param beginTime The inclusive beginning of the range of stats to include in the results. * @param endTime The exclusive end of the range of stats to include in the results. - * @return An {@link android.util.ArrayMap} keyed by package name or null if no stats are + * @return A {@link java.util.Map} keyed by package name, or null if no stats are * available. */ - public ArrayMap<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) { + public Map<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) { List<UsageStats> stats = queryUsageStats(INTERVAL_BEST, beginTime, endTime); if (stats.isEmpty()) { - @SuppressWarnings("unchecked") - ArrayMap<String, UsageStats> emptyStats = ArrayMap.EMPTY; - return emptyStats; + return Collections.emptyMap(); } ArrayMap<String, UsageStats> aggregatedStats = new ArrayMap<>(); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index b7d7c25..f979a0c 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1486,8 +1486,6 @@ public abstract class Context { * @see #sendBroadcast(Intent) * @see #sendBroadcast(Intent, String) * @see #sendOrderedBroadcast(Intent, String) - * @see #sendStickyBroadcast(Intent) - * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) * @see android.content.BroadcastReceiver * @see #registerReceiver * @see android.app.Activity#RESULT_OK @@ -1584,7 +1582,7 @@ public abstract class Context { @Nullable Bundle initialExtras); /** - * Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the + * <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the * Intent you are sending stays around after the broadcast is complete, * so that others can quickly retrieve that data through the return * value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}. In @@ -1595,6 +1593,12 @@ public abstract class Context { * permission in order to use this API. If you do not hold that * permission, {@link SecurityException} will be thrown. * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em> + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast, and the Intent will be held to * be re-broadcast to future receivers. @@ -1602,10 +1606,11 @@ public abstract class Context { * @see #sendBroadcast(Intent) * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) */ + @Deprecated public abstract void sendStickyBroadcast(Intent intent); /** - * Version of {@link #sendStickyBroadcast} that allows you to + * <p>Version of {@link #sendStickyBroadcast} that allows you to * receive data back from the broadcast. This is accomplished by * supplying your own BroadcastReceiver when calling, which will be * treated as a final receiver at the end of the broadcast -- its @@ -1622,6 +1627,12 @@ public abstract class Context { * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em> + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast. * @param resultReceiver Your own BroadcastReceiver to treat as the final @@ -1644,31 +1655,45 @@ public abstract class Context { * @see #registerReceiver * @see android.app.Activity#RESULT_OK */ + @Deprecated public abstract void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, @Nullable Handler scheduler, int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras); /** - * Remove the data previously sent with {@link #sendStickyBroadcast}, + * <p>Remove the data previously sent with {@link #sendStickyBroadcast}, * so that it is as if the sticky broadcast had never happened. * * <p>You must hold the {@link android.Manifest.permission#BROADCAST_STICKY} * permission in order to use this API. If you do not hold that * permission, {@link SecurityException} will be thrown. * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em> + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * * @param intent The Intent that was previously broadcast. * * @see #sendStickyBroadcast */ + @Deprecated public abstract void removeStickyBroadcast(Intent intent); /** - * Version of {@link #sendStickyBroadcast(Intent)} that allows you to specify the + * <p>Version of {@link #sendStickyBroadcast(Intent)} that allows you to specify the * user the broadcast will be sent to. This is not available to applications * that are not pre-installed on the system image. Using it requires holding * the INTERACT_ACROSS_USERS permission. * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em> + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast, and the Intent will be held to * be re-broadcast to future receivers. @@ -1676,10 +1701,11 @@ public abstract class Context { * * @see #sendBroadcast(Intent) */ + @Deprecated public abstract void sendStickyBroadcastAsUser(Intent intent, UserHandle user); /** - * Version of + * <p>Version of * {@link #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)} * that allows you to specify the * user the broadcast will be sent to. This is not available to applications @@ -1688,6 +1714,12 @@ public abstract class Context { * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em> + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast. * @param user UserHandle to send the intent to. @@ -1705,13 +1737,14 @@ public abstract class Context { * * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) */ + @Deprecated public abstract void sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user, BroadcastReceiver resultReceiver, @Nullable Handler scheduler, int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras); /** - * Version of {@link #removeStickyBroadcast(Intent)} that allows you to specify the + * <p>Version of {@link #removeStickyBroadcast(Intent)} that allows you to specify the * user the broadcast will be sent to. This is not available to applications * that are not pre-installed on the system image. Using it requires holding * the INTERACT_ACROSS_USERS permission. @@ -1720,11 +1753,18 @@ public abstract class Context { * permission in order to use this API. If you do not hold that * permission, {@link SecurityException} will be thrown. * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em> + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * * @param intent The Intent that was previously broadcast. * @param user UserHandle to remove the sticky broadcast from. * * @see #sendStickyBroadcastAsUser */ + @Deprecated public abstract void removeStickyBroadcastAsUser(Intent intent, UserHandle user); /** diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 44478d4..3e1f60a 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -32,6 +32,7 @@ import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; +import android.content.pm.KeySet; import android.content.pm.PackageInfo; import android.content.pm.ManifestDigest; import android.content.pm.PackageCleanItem; @@ -198,6 +199,14 @@ interface IPackageManager { in VerificationParams verificationParams, in String packageAbiOverride); + void installPackageAsUser(in String originPath, + in IPackageInstallObserver2 observer, + int flags, + in String installerPackageName, + in VerificationParams verificationParams, + in String packageAbiOverride, + int userId); + void finishPackageInstall(int token); void setInstallerPackageName(in String targetPackage, in String installerPackageName); @@ -446,8 +455,8 @@ interface IPackageManager { boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId); boolean getBlockUninstallForUser(String packageName, int userId); - IBinder getKeySetByAlias(String packageName, String alias); - IBinder getSigningKeySet(String packageName); - boolean isPackageSignedByKeySet(String packageName, IBinder ks); - boolean isPackageSignedByKeySetExactly(String packageName, IBinder ks); + KeySet getKeySetByAlias(String packageName, String alias); + KeySet getSigningKeySet(String packageName); + boolean isPackageSignedByKeySet(String packageName, in KeySet ks); + boolean isPackageSignedByKeySetExactly(String packageName, in KeySet ks); } diff --git a/core/java/android/content/pm/KeySet.aidl b/core/java/android/content/pm/KeySet.aidl new file mode 100644 index 0000000..493d288 --- /dev/null +++ b/core/java/android/content/pm/KeySet.aidl @@ -0,0 +1,19 @@ +/** + * 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 android.content.pm; + +parcelable KeySet;
\ No newline at end of file diff --git a/core/java/android/content/pm/KeySet.java b/core/java/android/content/pm/KeySet.java index fcdaa18..643db7e 100644 --- a/core/java/android/content/pm/KeySet.java +++ b/core/java/android/content/pm/KeySet.java @@ -17,13 +17,16 @@ package android.content.pm; import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; /** * Represents a {@code KeySet} that has been declared in the AndroidManifest.xml * file for the application. A {@code KeySet} can be used explicitly to * represent a trust relationship with other applications on the device. + * @hide */ -public class KeySet { +public class KeySet implements Parcelable { private IBinder token; @@ -40,6 +43,7 @@ public class KeySet { return token; } + /** @hide */ @Override public boolean equals(Object o) { if (o instanceof KeySet) { @@ -48,4 +52,58 @@ public class KeySet { } return false; } + + /** @hide */ + @Override + public int hashCode() { + return token.hashCode(); + } + + /** + * Implement Parcelable + * @hide + */ + public static final Parcelable.Creator<KeySet> CREATOR + = new Parcelable.Creator<KeySet>() { + + /** + * Create a KeySet from a Parcel + * + * @param in The parcel containing the KeySet + */ + public KeySet createFromParcel(Parcel source) { + return readFromParcel(source); + } + + /** + * Create an array of null KeySets + */ + public KeySet[] newArray(int size) { + return new KeySet[size]; + } + }; + + /** + * @hide + */ + private static KeySet readFromParcel(Parcel in) { + IBinder token = in.readStrongBinder(); + return new KeySet(token); + } + + /** + * @hide + */ + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeStrongBinder(token); + } + + /** + * @hide + */ + @Override + public int describeContents() { + return 0; + } }
\ No newline at end of file diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index fa2bb4d..1b15ff5 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3698,10 +3698,13 @@ public abstract class PackageManager { * * @param alias The alias for a given {@link KeySet} as defined in the * application's AndroidManifest.xml. + * @hide */ public abstract KeySet getKeySetByAlias(String packageName, String alias); - /** Return the signing {@link KeySet} for this application. */ + /** Return the signing {@link KeySet} for this application. + * @hide + */ public abstract KeySet getSigningKeySet(String packageName); /** @@ -3709,6 +3712,7 @@ public abstract class PackageManager { * of the keys specified by the {@link KeySet} ks. This will return true if * the package has been signed by additional keys (a superset) as well. * Compare to {@link #isSignedByExactly(String packageName, KeySet ks)}. + * @hide */ public abstract boolean isSignedBy(String packageName, KeySet ks); @@ -3716,6 +3720,7 @@ public abstract class PackageManager { * Return whether the package denoted by packageName has been signed by all * of, and only, the keys specified by the {@link KeySet} ks. Compare to * {@link #isSignedBy(String packageName, KeySet ks)}. + * @hide */ public abstract boolean isSignedByExactly(String packageName, KeySet ks); diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 3c9b7b3..a57b361 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -1575,6 +1575,13 @@ public abstract class CameraMetadata<TKey> { */ public static final int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17; + /** + * <p>Turn on custom high dynamic range (HDR) mode.</p> + * @see CaptureRequest#CONTROL_SCENE_MODE + * @hide + */ + public static final int CONTROL_SCENE_MODE_HDR = 18; + // // Enumeration values for CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE // diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java index ec4bc7d..0895fe3 100644 --- a/core/java/android/hardware/camera2/TotalCaptureResult.java +++ b/core/java/android/hardware/camera2/TotalCaptureResult.java @@ -19,6 +19,7 @@ package android.hardware.camera2; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.impl.CaptureResultExtras; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -48,13 +49,23 @@ import java.util.List; */ public final class TotalCaptureResult extends CaptureResult { + private final List<CaptureResult> mPartialResults; + /** - * Takes ownership of the passed-in properties object + * Takes ownership of the passed-in camera metadata and the partial results + * + * @param partials a list of partial results; {@code null} will be substituted for an empty list * @hide */ public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent, - CaptureResultExtras extras) { + CaptureResultExtras extras, List<CaptureResult> partials) { super(results, parent, extras); + + if (partials == null) { + mPartialResults = new ArrayList<>(); + } else { + mPartialResults = partials; + } } /** @@ -65,6 +76,8 @@ public final class TotalCaptureResult extends CaptureResult { */ public TotalCaptureResult(CameraMetadataNative results, int sequenceId) { super(results, sequenceId); + + mPartialResults = new ArrayList<>(); } /** @@ -73,14 +86,13 @@ public final class TotalCaptureResult extends CaptureResult { * <p>The list is returned is unmodifiable; attempting to modify it will result in a * {@code UnsupportedOperationException} being thrown.</p> * - * <p>The list size will be inclusive between {@code 1} and - * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT}, in ascending order + * <p>The list size will be inclusive between {@code 0} and + * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT}, with elements in ascending order * of when {@link CameraCaptureSession.CaptureListener#onCaptureProgressed} was invoked.</p> * * @return unmodifiable list of partial results */ public List<CaptureResult> getPartialResults() { - // TODO - return Collections.unmodifiableList(null); + return Collections.unmodifiableList(mPartialResults); } } diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index d75dfe6..f5666bf 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -41,6 +41,7 @@ import android.view.Surface; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -967,6 +968,8 @@ public class CameraDeviceImpl extends CameraDevice { private long mCompletedFrameNumber = -1; private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>(); + /** Map frame numbers to list of partial results */ + private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>(); private void update() { Iterator<Long> iter = mFutureErrorSet.iterator(); @@ -983,8 +986,8 @@ public class CameraDeviceImpl extends CameraDevice { /** * This function is called every time when a result or an error is received. - * @param frameNumber: the frame number corresponding to the result or error - * @param isError: true if it is an error, false if it is not an error + * @param frameNumber the frame number corresponding to the result or error + * @param isError true if it is an error, false if it is not an error */ public void updateTracker(long frameNumber, boolean isError) { if (isError) { @@ -1006,6 +1009,55 @@ public class CameraDeviceImpl extends CameraDevice { update(); } + /** + * This function is called every time a result has been completed. + * + * <p>It keeps a track of all the partial results already created for a particular + * frame number.</p> + * + * @param frameNumber the frame number corresponding to the result + * @param result the total or partial result + * @param partial {@true} if the result is partial, {@code false} if total + */ + public void updateTracker(long frameNumber, CaptureResult result, boolean partial) { + + if (!partial) { + // Update the total result's frame status as being successful + updateTracker(frameNumber, /*isError*/false); + // Don't keep a list of total results, we don't need to track them + return; + } + + if (result == null) { + // Do not record blank results; this also means there will be no total result + // so it doesn't matter that the partials were not recorded + return; + } + + // Partial results must be aggregated in-order for that frame number + List<CaptureResult> partials = mPartialResults.get(frameNumber); + if (partials == null) { + partials = new ArrayList<>(); + mPartialResults.put(frameNumber, partials); + } + + partials.add(result); + } + + /** + * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}. + * + * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker} + * is called again with new partials for that frame number).</p> + * + * @param frameNumber the frame number corresponding to the result + * @return a list of partial results for that frame with at least 1 element, + * or {@code null} if there were no partials recorded for that frame + */ + public List<CaptureResult> popPartialResults(long frameNumber) { + return mPartialResults.remove(frameNumber); + } + public long getCompletedFrameNumber() { return mCompletedFrameNumber; } @@ -1244,12 +1296,6 @@ public class CameraDeviceImpl extends CameraDevice { boolean isPartialResult = (resultExtras.getPartialResultCount() < mTotalPartialCount); - // Update tracker (increment counter) when it's not a partial result. - if (!isPartialResult) { - mFrameNumberTracker.updateTracker(frameNumber, - /*error*/false); - } - // Check if we have a listener for this if (holder == null) { if (DEBUG) { @@ -1257,6 +1303,9 @@ public class CameraDeviceImpl extends CameraDevice { "holder is null, early return at frame " + frameNumber); } + + mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult); + return; } @@ -1266,6 +1315,8 @@ public class CameraDeviceImpl extends CameraDevice { "camera is closed, early return at frame " + frameNumber); } + + mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult); return; } @@ -1273,6 +1324,8 @@ public class CameraDeviceImpl extends CameraDevice { Runnable resultDispatch = null; + CaptureResult finalResult; + // Either send a partial result or the final capture completed result if (isPartialResult) { final CaptureResult resultAsCapture = @@ -1290,9 +1343,14 @@ public class CameraDeviceImpl extends CameraDevice { } } }; + + finalResult = resultAsCapture; } else { + List<CaptureResult> partialResults = + mFrameNumberTracker.popPartialResults(frameNumber); + final TotalCaptureResult resultAsCapture = - new TotalCaptureResult(result, request, resultExtras); + new TotalCaptureResult(result, request, resultExtras, partialResults); // Final capture result resultDispatch = new Runnable() { @@ -1306,10 +1364,15 @@ public class CameraDeviceImpl extends CameraDevice { } } }; + + finalResult = resultAsCapture; } holder.getHandler().post(resultDispatch); + // Collect the partials for a total result; or mark the frame as totally completed + mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult); + // Fire onCaptureSequenceCompleted if (!isPartialResult) { checkAndFireSequenceComplete(); diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index febb015..f47ce79 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -67,7 +67,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.HashMap; -import java.util.List; /** * Implementation of camera metadata marshal/unmarshal across Binder to @@ -655,6 +654,15 @@ public class CameraMetadataNative implements Parcelable { private Face[] getFaces() { Integer faceDetectMode = get(CaptureResult.STATISTICS_FACE_DETECT_MODE); + byte[] faceScores = get(CaptureResult.STATISTICS_FACE_SCORES); + Rect[] faceRectangles = get(CaptureResult.STATISTICS_FACE_RECTANGLES); + int[] faceIds = get(CaptureResult.STATISTICS_FACE_IDS); + int[] faceLandmarks = get(CaptureResult.STATISTICS_FACE_LANDMARKS); + + if (areValuesAllNull(faceDetectMode, faceScores, faceRectangles, faceIds, faceLandmarks)) { + return null; + } + if (faceDetectMode == null) { Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE"); faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE; @@ -670,8 +678,6 @@ public class CameraMetadataNative implements Parcelable { } // Face scores and rectangles are required by SIMPLE and FULL mode. - byte[] faceScores = get(CaptureResult.STATISTICS_FACE_SCORES); - Rect[] faceRectangles = get(CaptureResult.STATISTICS_FACE_RECTANGLES); if (faceScores == null || faceRectangles == null) { Log.w(TAG, "Expect face scores and rectangles to be non-null"); return new Face[0]; @@ -683,8 +689,6 @@ public class CameraMetadataNative implements Parcelable { // To be safe, make number of faces is the minimal of all face info metadata length. int numFaces = Math.min(faceScores.length, faceRectangles.length); // Face id and landmarks are only required by FULL mode. - int[] faceIds = get(CaptureResult.STATISTICS_FACE_IDS); - int[] faceLandmarks = get(CaptureResult.STATISTICS_FACE_LANDMARKS); if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) { if (faceIds == null || faceLandmarks == null) { Log.w(TAG, "Expect face ids and landmarks to be non-null for FULL mode," + @@ -755,22 +759,32 @@ public class CameraMetadataNative implements Parcelable { private LensShadingMap getLensShadingMap() { float[] lsmArray = getBase(CaptureResult.STATISTICS_LENS_SHADING_MAP); + Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE); + + // Do not warn if lsmArray is null while s is not. This is valid. if (lsmArray == null) { - Log.w(TAG, "getLensShadingMap - Lens shading map was null."); return null; } - Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE); + + if (s == null) { + Log.w(TAG, "getLensShadingMap - Lens shading map size was null."); + return null; + } + LensShadingMap map = new LensShadingMap(lsmArray, s.getHeight(), s.getWidth()); return map; } private Location getGpsLocation() { String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD); - Location l = new Location(translateProcessToLocationProvider(processingMethod)); - double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES); Long timeStamp = get(CaptureResult.JPEG_GPS_TIMESTAMP); + if (areValuesAllNull(processingMethod, coords, timeStamp)) { + return null; + } + + Location l = new Location(translateProcessToLocationProvider(processingMethod)); if (timeStamp != null) { l.setTime(timeStamp); } else { @@ -873,7 +887,13 @@ public class CameraMetadataNative implements Parcelable { float[] red = getBase(CaptureRequest.TONEMAP_CURVE_RED); float[] green = getBase(CaptureRequest.TONEMAP_CURVE_GREEN); float[] blue = getBase(CaptureRequest.TONEMAP_CURVE_BLUE); + + if (areValuesAllNull(red, green, blue)) { + return null; + } + if (red == null || green == null || blue == null) { + Log.w(TAG, "getTonemapCurve - missing tone curve components"); return null; } TonemapCurve tc = new TonemapCurve(red, green, blue); @@ -1208,6 +1228,18 @@ public class CameraMetadataNative implements Parcelable { } } + /** Check if input arguments are all {@code null}. + * + * @param objs Input arguments for null check + * @return {@code true} if input arguments are all {@code null}, otherwise {@code false} + */ + private static boolean areValuesAllNull(Object... objs) { + for (Object o : objs) { + if (o != null) return false; + } + return true; + } + static { /* * We use a class initializer to allow the native code to cache some field offsets diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java index ee0ca9c..a8d1018 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java @@ -882,6 +882,7 @@ public class LegacyMetadataMapper { Parameters.SCENE_MODE_PARTY, Parameters.SCENE_MODE_CANDLELIGHT, Parameters.SCENE_MODE_BARCODE, + Parameters.SCENE_MODE_HDR, }; private final static int[] sSceneModes = { @@ -901,6 +902,7 @@ public class LegacyMetadataMapper { CameraCharacteristics.CONTROL_SCENE_MODE_PARTY, CameraCharacteristics.CONTROL_SCENE_MODE_CANDLELIGHT, CameraCharacteristics.CONTROL_SCENE_MODE_BARCODE, + CameraCharacteristics.CONTROL_SCENE_MODE_HDR, }; static int convertSceneModeFromLegacy(String mode) { diff --git a/core/java/android/net/NetworkBoundURLFactory.java b/core/java/android/net/NetworkBoundURLFactory.java deleted file mode 100644 index 356100e..0000000 --- a/core/java/android/net/NetworkBoundURLFactory.java +++ /dev/null @@ -1,35 +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 android.net; - -import java.net.MalformedURLException; -import java.net.URL; - -/** - * An interface that describes a factory for network-specific {@link URL} objects. - */ -public interface NetworkBoundURLFactory { - /** - * Returns a {@link URL} based on the given URL but bound to the specified {@code Network}, - * such that opening the URL will send all network traffic on the specified Network. - * - * @return a {@link URL} bound to this {@code Network}. - * @throws MalformedURLException if the URL was not valid, or this factory cannot handle the - * specified URL (e.g., if it does not support the protocol of the URL). - */ - public URL getBoundURL(Network network, URL url) throws MalformedURLException; -} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ae11f47..33e0468 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -904,6 +904,15 @@ public final class Settings { public static final String ACTION_APP_NOTIFICATION_SETTINGS = "android.settings.APP_NOTIFICATION_SETTINGS"; + /** + * Activity Action: Show notification redaction settings. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_APP_NOTIFICATION_REDACTION + = "android.settings.ACTION_APP_NOTIFICATION_REDACTION"; + /** @hide */ public static final String EXTRA_APP_UID = "app_uid"; /** @hide */ public static final String EXTRA_APP_PACKAGE = "app_package"; @@ -3714,6 +3723,14 @@ public final class Settings { "lock_screen_allow_private_notifications"; /** + * Set by the system to track if the user needs to see the call to action for + * the lockscreen notification policy. + * @hide + */ + public static final String SHOW_NOTE_ABOUT_NOTIFICATION_HIDING = + "show_note_about_notification_hiding"; + + /** * The Logging ID (a unique 64-bit value) as a hex string. * Used as a pseudonymous identifier for logging. * @deprecated This identifier is poorly initialized and has diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java index f8bf45b..7e9aba0 100644 --- a/core/java/android/security/IKeystoreService.java +++ b/core/java/android/security/IKeystoreService.java @@ -478,6 +478,59 @@ public interface IKeystoreService extends IInterface { } return _result; } + + public int reset_uid(int uid) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + int _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeInt(uid); + mRemote.transact(Stub.TRANSACTION_reset_uid, _data, _reply, 0); + _reply.readException(); + _result = _reply.readInt(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } + + public int sync_uid(int srcUid, int dstUid) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + int _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeInt(srcUid); + _data.writeInt(dstUid); + mRemote.transact(Stub.TRANSACTION_sync_uid, _data, _reply, 0); + _reply.readException(); + _result = _reply.readInt(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } + + public int password_uid(String password, int uid) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + int _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeString(password); + _data.writeInt(uid); + mRemote.transact(Stub.TRANSACTION_password_uid, _data, _reply, 0); + _reply.readException(); + _result = _reply.readInt(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } } private static final String DESCRIPTOR = "android.security.keystore"; @@ -505,6 +558,9 @@ public interface IKeystoreService extends IInterface { static final int TRANSACTION_duplicate = IBinder.FIRST_CALL_TRANSACTION + 20; static final int TRANSACTION_is_hardware_backed = IBinder.FIRST_CALL_TRANSACTION + 21; static final int TRANSACTION_clear_uid = IBinder.FIRST_CALL_TRANSACTION + 22; + static final int TRANSACTION_reset_uid = IBinder.FIRST_CALL_TRANSACTION + 23; + static final int TRANSACTION_sync_uid = IBinder.FIRST_CALL_TRANSACTION + 24; + static final int TRANSACTION_password_uid = IBinder.FIRST_CALL_TRANSACTION + 25; /** * Cast an IBinder object into an IKeystoreService interface, generating @@ -597,4 +653,10 @@ public interface IKeystoreService extends IInterface { public int is_hardware_backed(String string) throws RemoteException; public int clear_uid(long uid) throws RemoteException; + + public int reset_uid(int uid) throws RemoteException; + + public int sync_uid(int sourceUid, int targetUid) throws RemoteException; + + public int password_uid(String password, int uid) throws RemoteException; } diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index a544b2d..cb0bcf2 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -81,6 +81,33 @@ public abstract class NotificationListenerService extends Service { * This does not change the interruption filter, only the effects. **/ public static final int HINT_HOST_DISABLE_EFFECTS = 1; + /** + * The full trim of the StatusBarNotification including all its features. + * + * @hide + */ + @SystemApi + public static final int TRIM_FULL = 0; + + /** + * A light trim of the StatusBarNotification excluding the following features: + * + * <ol> + * <li>{@link Notification#tickerView tickerView}</li> + * <li>{@link Notification#contentView contentView}</li> + * <li>{@link Notification#largeIcon largeIcon}</li> + * <li>{@link Notification#bigContentView bigContentView}</li> + * <li>{@link Notification#headsUpContentView headsUpContentView}</li> + * <li>{@link Notification#EXTRA_LARGE_ICON extras[EXTRA_LARGE_ICON]}</li> + * <li>{@link Notification#EXTRA_LARGE_ICON_BIG extras[EXTRA_LARGE_ICON_BIG]}</li> + * <li>{@link Notification#EXTRA_PICTURE extras[EXTRA_PICTURE]}</li> + * </ol> + * + * @hide + */ + @SystemApi + public static final int TRIM_LIGHT = 1; + private INotificationListenerWrapper mWrapper = null; private RankingMap mRankingMap; @@ -314,13 +341,53 @@ public abstract class NotificationListenerService extends Service { } /** + * Sets the notification trim that will be received via {@link #onNotificationPosted}. + * + * <p> + * Setting a trim other than {@link #TRIM_FULL} enables listeners that don't need access to the + * full notification features right away to reduce their memory footprint. Full notifications + * can be requested on-demand via {@link #getActiveNotifications(int)}. + * + * <p> + * Set to {@link #TRIM_FULL} initially. + * + * @hide + * + * @param trim trim of the notifications to be passed via {@link #onNotificationPosted}. + * See <code>TRIM_*</code> constants. + */ + @SystemApi + public final void setOnNotificationPostedTrim(int trim) { + if (!isBound()) return; + try { + getNotificationInterface().setOnNotificationPostedTrimFromListener(mWrapper, trim); + } catch (RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + } + } + + /** * Request the list of outstanding notifications (that is, those that are visible to the * current user). Useful when you don't know what's already been posted. * * @return An array of active notifications, sorted in natural order. */ public StatusBarNotification[] getActiveNotifications() { - return getActiveNotifications(null); + return getActiveNotifications(null, TRIM_FULL); + } + + /** + * Request the list of outstanding notifications (that is, those that are visible to the + * current user). Useful when you don't know what's already been posted. + * + * @hide + * + * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants. + * @return An array of active notifications, sorted in natural order. + */ + @SystemApi + public StatusBarNotification[] getActiveNotifications(int trim) { + return getActiveNotifications(null, trim); } /** @@ -328,14 +395,33 @@ public abstract class NotificationListenerService extends Service { * notifications but didn't want to retain the bits, and now need to go back and extract * more data out of those notifications. * + * @param keys the keys of the notifications to request * @return An array of notifications corresponding to the requested keys, in the * same order as the key list. */ public StatusBarNotification[] getActiveNotifications(String[] keys) { - if (!isBound()) return null; + return getActiveNotifications(keys, TRIM_FULL); + } + + /** + * Request one or more notifications by key. Useful if you have been keeping track of + * notifications but didn't want to retain the bits, and now need to go back and extract + * more data out of those notifications. + * + * @hide + * + * @param keys the keys of the notifications to request + * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants. + * @return An array of notifications corresponding to the requested keys, in the + * same order as the key list. + */ + @SystemApi + public StatusBarNotification[] getActiveNotifications(String[] keys, int trim) { + if (!isBound()) + return null; try { - ParceledListSlice<StatusBarNotification> parceledList = - getNotificationInterface().getActiveNotificationsFromListener(mWrapper, keys); + ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface() + .getActiveNotificationsFromListener(mWrapper, keys, trim); List<StatusBarNotification> list = parceledList.getList(); int N = list.size(); diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java index ebb1a5c..eb17429 100644 --- a/core/java/android/transition/ChangeBounds.java +++ b/core/java/android/transition/ChangeBounds.java @@ -31,6 +31,7 @@ import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.util.IntProperty; import android.util.Property; import android.view.View; import android.view.ViewGroup; @@ -185,25 +186,36 @@ public class ChangeBounds extends Transition { } if (numChanges > 0) { if (!mResizeClip) { - if (startLeft != endLeft) view.setLeft(startLeft); - if (startTop != endTop) view.setTop(startTop); - if (startRight != endRight) view.setRight(startRight); - if (startBottom != endBottom) view.setBottom(startBottom); - ObjectAnimator topLeftAnimator = null; - if (startLeft != endLeft || startTop != endTop) { - Path topLeftPath = getPathMotion().getPath(startLeft, startTop, - endLeft, endTop); - topLeftAnimator = ObjectAnimator.ofInt(view, "left", "top", topLeftPath); - } - ObjectAnimator bottomRightAnimator = null; - if (startRight != endRight || startBottom != endBottom) { - Path bottomRightPath = getPathMotion().getPath(startRight, startBottom, - endRight, endBottom); - bottomRightAnimator = ObjectAnimator.ofInt(view, "right", "bottom", - bottomRightPath); + Animator anim; + if (startWidth == endWidth && startHeight == endHeight) { + view.offsetLeftAndRight(startLeft - view.getLeft()); + view.offsetTopAndBottom(startTop - view.getTop()); + Path positionPath = getPathMotion().getPath(0, 0, endLeft - startLeft, + endTop - startTop); + anim = ObjectAnimator.ofInt(view, new HorizontalOffsetProperty(), + new VerticalOffsetProperty(), positionPath); + } else { + if (startLeft != endLeft) view.setLeft(startLeft); + if (startTop != endTop) view.setTop(startTop); + if (startRight != endRight) view.setRight(startRight); + if (startBottom != endBottom) view.setBottom(startBottom); + ObjectAnimator topLeftAnimator = null; + if (startLeft != endLeft || startTop != endTop) { + Path topLeftPath = getPathMotion().getPath(startLeft, startTop, + endLeft, endTop); + topLeftAnimator = ObjectAnimator + .ofInt(view, "left", "top", topLeftPath); + } + ObjectAnimator bottomRightAnimator = null; + if (startRight != endRight || startBottom != endBottom) { + Path bottomRightPath = getPathMotion().getPath(startRight, startBottom, + endRight, endBottom); + bottomRightAnimator = ObjectAnimator.ofInt(view, "right", "bottom", + bottomRightPath); + } + anim = TransitionUtils.mergeAnimators(topLeftAnimator, + bottomRightAnimator); } - Animator anim = TransitionUtils.mergeAnimators(topLeftAnimator, - bottomRightAnimator); if (view.getParent() instanceof ViewGroup) { final ViewGroup parent = (ViewGroup) view.getParent(); parent.suppressLayout(true); @@ -341,4 +353,48 @@ public class ChangeBounds extends Transition { } return null; } + + private abstract static class OffsetProperty extends IntProperty<View> { + int mPreviousValue; + + public OffsetProperty(String name) { + super(name); + } + + @Override + public void setValue(View view, int value) { + int offset = value - mPreviousValue; + offsetBy(view, offset); + mPreviousValue = value; + } + + @Override + public Integer get(View object) { + return null; + } + + protected abstract void offsetBy(View view, int by); + } + + private static class HorizontalOffsetProperty extends OffsetProperty { + public HorizontalOffsetProperty() { + super("offsetLeftAndRight"); + } + + @Override + protected void offsetBy(View view, int by) { + view.offsetLeftAndRight(by); + } + } + + private static class VerticalOffsetProperty extends OffsetProperty { + public VerticalOffsetProperty() { + super("offsetTopAndBottom"); + } + + @Override + protected void offsetBy(View view, int by) { + view.offsetTopAndBottom(by); + } + } } diff --git a/core/java/android/util/Spline.java b/core/java/android/util/Spline.java index ed027eb..41a2e5d 100644 --- a/core/java/android/util/Spline.java +++ b/core/java/android/util/Spline.java @@ -20,15 +20,34 @@ package android.util; * Performs spline interpolation given a set of control points. * @hide */ -public final class Spline { - private final float[] mX; - private final float[] mY; - private final float[] mM; - - private Spline(float[] x, float[] y, float[] m) { - mX = x; - mY = y; - mM = m; +public abstract class Spline { + + /** + * Interpolates the value of Y = f(X) for given X. + * Clamps X to the domain of the spline. + * + * @param x The X value. + * @return The interpolated Y = f(X) value. + */ + public abstract float interpolate(float x); + + /** + * Creates an appropriate spline based on the properties of the control points. + * + * If the control points are monotonic then the resulting spline will preserve that and + * otherwise optimize for error bounds. + */ + public static Spline createSpline(float[] x, float[] y) { + if (!isStrictlyIncreasing(x)) { + throw new IllegalArgumentException("The control points must all have strictly " + + "increasing X values."); + } + + if (isMonotonic(y)) { + return createMonotoneCubicSpline(x, y); + } else { + return createLinearSpline(x, y); + } } /** @@ -50,107 +69,229 @@ public final class Spline { * @throws IllegalArgumentException if the control points are not monotonic. */ public static Spline createMonotoneCubicSpline(float[] x, float[] y) { - if (x == null || y == null || x.length != y.length || x.length < 2) { - throw new IllegalArgumentException("There must be at least two control " - + "points and the arrays must be of equal length."); - } + return new MonotoneCubicSpline(x, y); + } - final int n = x.length; - float[] d = new float[n - 1]; // could optimize this out - float[] m = new float[n]; + /** + * Creates a linear spline from a given set of control points. + * + * Like a monotone cubic spline, the interpolated curve will be monotonic if the control points + * are monotonic. + * + * @param x The X component of the control points, strictly increasing. + * @param y The Y component of the control points. + * @return + * + * @throws IllegalArgumentException if the X or Y arrays are null, have + * different lengths or have fewer than 2 values. + * @throws IllegalArgumentException if the X components of the control points are not strictly + * increasing. + */ + public static Spline createLinearSpline(float[] x, float[] y) { + return new LinearSpline(x, y); + } - // Compute slopes of secant lines between successive points. - for (int i = 0; i < n - 1; i++) { - float h = x[i + 1] - x[i]; - if (h <= 0f) { - throw new IllegalArgumentException("The control points must all " - + "have strictly increasing X values."); + private static boolean isStrictlyIncreasing(float[] x) { + if (x == null || x.length < 2) { + throw new IllegalArgumentException("There must be at least two control points."); + } + float prev = x[0]; + for (int i = 1; i < x.length; i++) { + float curr = x[i]; + if (curr <= prev) { + return false; } - d[i] = (y[i + 1] - y[i]) / h; + prev = curr; } + return true; + } - // Initialize the tangents as the average of the secants. - m[0] = d[0]; - for (int i = 1; i < n - 1; i++) { - m[i] = (d[i - 1] + d[i]) * 0.5f; + private static boolean isMonotonic(float[] x) { + if (x == null || x.length < 2) { + throw new IllegalArgumentException("There must be at least two control points."); } - m[n - 1] = d[n - 2]; - - // Update the tangents to preserve monotonicity. - for (int i = 0; i < n - 1; i++) { - if (d[i] == 0f) { // successive Y values are equal - m[i] = 0f; - m[i + 1] = 0f; - } else { - float a = m[i] / d[i]; - float b = m[i + 1] / d[i]; - if (a < 0f || b < 0f) { - throw new IllegalArgumentException("The control points must have " - + "monotonic Y values."); - } - float h = FloatMath.hypot(a, b); - if (h > 9f) { - float t = 3f / h; - m[i] = t * a * d[i]; - m[i + 1] = t * b * d[i]; - } + float prev = x[0]; + for (int i = 1; i < x.length; i++) { + float curr = x[i]; + if (curr < prev) { + return false; } + prev = curr; } - return new Spline(x, y, m); + return true; } - /** - * Interpolates the value of Y = f(X) for given X. - * Clamps X to the domain of the spline. - * - * @param x The X value. - * @return The interpolated Y = f(X) value. - */ - public float interpolate(float x) { - // Handle the boundary cases. - final int n = mX.length; - if (Float.isNaN(x)) { - return x; + public static class MonotoneCubicSpline extends Spline { + private float[] mX; + private float[] mY; + private float[] mM; + + public MonotoneCubicSpline(float[] x, float[] y) { + if (x == null || y == null || x.length != y.length || x.length < 2) { + throw new IllegalArgumentException("There must be at least two control " + + "points and the arrays must be of equal length."); + } + + final int n = x.length; + float[] d = new float[n - 1]; // could optimize this out + float[] m = new float[n]; + + // Compute slopes of secant lines between successive points. + for (int i = 0; i < n - 1; i++) { + float h = x[i + 1] - x[i]; + if (h <= 0f) { + throw new IllegalArgumentException("The control points must all " + + "have strictly increasing X values."); + } + d[i] = (y[i + 1] - y[i]) / h; + } + + // Initialize the tangents as the average of the secants. + m[0] = d[0]; + for (int i = 1; i < n - 1; i++) { + m[i] = (d[i - 1] + d[i]) * 0.5f; + } + m[n - 1] = d[n - 2]; + + // Update the tangents to preserve monotonicity. + for (int i = 0; i < n - 1; i++) { + if (d[i] == 0f) { // successive Y values are equal + m[i] = 0f; + m[i + 1] = 0f; + } else { + float a = m[i] / d[i]; + float b = m[i + 1] / d[i]; + if (a < 0f || b < 0f) { + throw new IllegalArgumentException("The control points must have " + + "monotonic Y values."); + } + float h = FloatMath.hypot(a, b); + if (h > 9f) { + float t = 3f / h; + m[i] = t * a * d[i]; + m[i + 1] = t * b * d[i]; + } + } + } + + mX = x; + mY = y; + mM = m; } - if (x <= mX[0]) { - return mY[0]; + + @Override + public float interpolate(float x) { + // Handle the boundary cases. + final int n = mX.length; + if (Float.isNaN(x)) { + return x; + } + if (x <= mX[0]) { + return mY[0]; + } + if (x >= mX[n - 1]) { + return mY[n - 1]; + } + + // Find the index 'i' of the last point with smaller X. + // We know this will be within the spline due to the boundary tests. + int i = 0; + while (x >= mX[i + 1]) { + i += 1; + if (x == mX[i]) { + return mY[i]; + } + } + + // Perform cubic Hermite spline interpolation. + float h = mX[i + 1] - mX[i]; + float t = (x - mX[i]) / h; + return (mY[i] * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t) + + (mY[i + 1] * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t; } - if (x >= mX[n - 1]) { - return mY[n - 1]; + + // For debugging. + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + final int n = mX.length; + str.append("MonotoneCubicSpline{["); + for (int i = 0; i < n; i++) { + if (i != 0) { + str.append(", "); + } + str.append("(").append(mX[i]); + str.append(", ").append(mY[i]); + str.append(": ").append(mM[i]).append(")"); + } + str.append("]}"); + return str.toString(); } + } - // Find the index 'i' of the last point with smaller X. - // We know this will be within the spline due to the boundary tests. - int i = 0; - while (x >= mX[i + 1]) { - i += 1; - if (x == mX[i]) { - return mY[i]; + public static class LinearSpline extends Spline { + private final float[] mX; + private final float[] mY; + private final float[] mM; + + public LinearSpline(float[] x, float[] y) { + if (x == null || y == null || x.length != y.length || x.length < 2) { + throw new IllegalArgumentException("There must be at least two control " + + "points and the arrays must be of equal length."); + } + final int N = x.length; + mM = new float[N-1]; + for (int i = 0; i < N-1; i++) { + mM[i] = (y[i+1] - y[i]) / (x[i+1] - x[i]); } + mX = x; + mY = y; } - // Perform cubic Hermite spline interpolation. - float h = mX[i + 1] - mX[i]; - float t = (x - mX[i]) / h; - return (mY[i] * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t) - + (mY[i + 1] * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t; - } + @Override + public float interpolate(float x) { + // Handle the boundary cases. + final int n = mX.length; + if (Float.isNaN(x)) { + return x; + } + if (x <= mX[0]) { + return mY[0]; + } + if (x >= mX[n - 1]) { + return mY[n - 1]; + } - // For debugging. - @Override - public String toString() { - StringBuilder str = new StringBuilder(); - final int n = mX.length; - str.append("["); - for (int i = 0; i < n; i++) { - if (i != 0) { - str.append(", "); - } - str.append("(").append(mX[i]); - str.append(", ").append(mY[i]); - str.append(": ").append(mM[i]).append(")"); + // Find the index 'i' of the last point with smaller X. + // We know this will be within the spline due to the boundary tests. + int i = 0; + while (x >= mX[i + 1]) { + i += 1; + if (x == mX[i]) { + return mY[i]; + } + } + return mY[i] + mM[i] * (x - mX[i]); + } + + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + final int n = mX.length; + str.append("LinearSpline{["); + for (int i = 0; i < n; i++) { + if (i != 0) { + str.append(", "); + } + str.append("(").append(mX[i]); + str.append(", ").append(mY[i]); + if (i < n-1) { + str.append(": ").append(mM[i]); + } + str.append(")"); + } + str.append("]}"); + return str.toString(); } - str.append("]"); - return str.toString(); } } diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index 413ea04..fa4a13a 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -87,8 +87,11 @@ public class RenderNodeAnimator extends Animator { private float mFinalValue; private TimeInterpolator mInterpolator; - private boolean mStarted = false; - private boolean mFinished = false; + private static final int STATE_PREPARE = 0; + private static final int STATE_DELAYED = 1; + private static final int STATE_RUNNING = 2; + private static final int STATE_FINISHED = 3; + private int mState = STATE_PREPARE; private long mUnscaledDuration = 300; private long mUnscaledStartDelay = 0; @@ -142,7 +145,7 @@ public class RenderNodeAnimator extends Animator { } private void checkMutable() { - if (mStarted) { + if (mState != STATE_PREPARE) { throw new IllegalStateException("Animator has already started, cannot change it now!"); } } @@ -170,11 +173,11 @@ public class RenderNodeAnimator extends Animator { throw new IllegalStateException("Missing target!"); } - if (mStarted) { + if (mState != STATE_PREPARE) { throw new IllegalStateException("Already started!"); } - mStarted = true; + mState = STATE_DELAYED; applyInterpolator(); if (mStartDelay <= 0 || !mUiThreadHandlesDelay) { @@ -186,6 +189,7 @@ public class RenderNodeAnimator extends Animator { } private void doStart() { + mState = STATE_RUNNING; nStart(mNativePtr.get(), this); // Alpha is a special snowflake that has the canonical value stored @@ -197,11 +201,7 @@ public class RenderNodeAnimator extends Animator { mViewTarget.mTransformationInfo.mAlpha = mFinalValue; } - final ArrayList<AnimatorListener> listeners = cloneListeners(); - final int numListeners = listeners == null ? 0 : listeners.size(); - for (int i = 0; i < numListeners; i++) { - listeners.get(i).onAnimationStart(this); - } + notifyStartListeners(); if (mViewTarget != null) { // Kick off a frame to start the process @@ -209,10 +209,21 @@ public class RenderNodeAnimator extends Animator { } } + private void notifyStartListeners() { + final ArrayList<AnimatorListener> listeners = cloneListeners(); + final int numListeners = listeners == null ? 0 : listeners.size(); + for (int i = 0; i < numListeners; i++) { + listeners.get(i).onAnimationStart(this); + } + } + @Override public void cancel() { - if (!mFinished) { - getHelper().removeDelayedAnimation(this); + if (mState != STATE_FINISHED) { + if (mState == STATE_DELAYED) { + getHelper().removeDelayedAnimation(this); + notifyStartListeners(); + } nEnd(mNativePtr.get()); final ArrayList<AnimatorListener> listeners = cloneListeners(); @@ -220,12 +231,17 @@ public class RenderNodeAnimator extends Animator { for (int i = 0; i < numListeners; i++) { listeners.get(i).onAnimationCancel(this); } + + if (mViewTarget != null) { + // Kick off a frame to flush the state change + mViewTarget.invalidateViewProperty(true, false); + } } } @Override public void end() { - if (!mFinished) { + if (mState != STATE_FINISHED) { nEnd(mNativePtr.get()); } } @@ -299,12 +315,12 @@ public class RenderNodeAnimator extends Animator { @Override public boolean isRunning() { - return mStarted && !mFinished; + return mState == STATE_DELAYED || mState == STATE_RUNNING; } @Override public boolean isStarted() { - return mStarted; + return mState != STATE_PREPARE; } @Override @@ -319,7 +335,11 @@ public class RenderNodeAnimator extends Animator { } protected void onFinished() { - mFinished = true; + if (mState == STATE_DELAYED) { + getHelper().removeDelayedAnimation(this); + notifyStartListeners(); + } + mState = STATE_FINISHED; final ArrayList<AnimatorListener> listeners = cloneListeners(); final int numListeners = listeners == null ? 0 : listeners.size(); diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 61b4567..b6e7353 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -23,7 +23,6 @@ import android.app.usage.UsageStatsManager; import android.os.AsyncTask; import android.provider.Settings; import android.text.TextUtils; -import android.util.ArrayMap; import android.util.Slog; import android.widget.AbsListView; import android.widget.GridView; @@ -73,6 +72,7 @@ import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -100,7 +100,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic private boolean mResolvingHome = false; private UsageStatsManager mUsm; - private ArrayMap<String, UsageStats> mStats; + private Map<String, UsageStats> mStats; private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14; private boolean mRegistered; diff --git a/core/res/res/color/btn_default_material_dark.xml b/core/res/res/color/btn_default_material_dark.xml index 7c904cd..9be1417 100644 --- a/core/res/res/color/btn_default_material_dark.xml +++ b/core/res/res/color/btn_default_material_dark.xml @@ -16,7 +16,7 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_enabled="false" - android:alpha="@dimen/disabled_alpha_material" - android:color="@color/button_material_dark"/> + android:alpha="@dimen/disabled_alpha_material_dark" + android:color="@color/button_material_dark"/> <item android:color="@color/button_material_dark"/> </selector> diff --git a/core/res/res/color/btn_default_material_light.xml b/core/res/res/color/btn_default_material_light.xml index 738f9ad..af5afe6 100644 --- a/core/res/res/color/btn_default_material_light.xml +++ b/core/res/res/color/btn_default_material_light.xml @@ -16,7 +16,7 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_enabled="false" - android:alpha="@dimen/disabled_alpha_material" - android:color="@color/button_material_light"/> + android:alpha="@dimen/disabled_alpha_material_light" + android:color="@color/button_material_light"/> <item android:color="@color/button_material_light"/> </selector> diff --git a/core/res/res/color/primary_text_disable_only_material_dark.xml b/core/res/res/color/primary_text_disable_only_material_dark.xml index cdae790..a6296c9 100644 --- a/core/res/res/color/primary_text_disable_only_material_dark.xml +++ b/core/res/res/color/primary_text_disable_only_material_dark.xml @@ -16,7 +16,7 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_enabled="false" - android:alpha="@dimen/disabled_alpha_material" - android:color="@color/bright_foreground_material_dark"/> + android:alpha="@dimen/disabled_alpha_material_dark" + android:color="@color/bright_foreground_material_dark"/> <item android:color="@color/bright_foreground_material_dark"/> </selector> diff --git a/core/res/res/color/primary_text_disable_only_material_light.xml b/core/res/res/color/primary_text_disable_only_material_light.xml index 0bf14d0..b781844 100644 --- a/core/res/res/color/primary_text_disable_only_material_light.xml +++ b/core/res/res/color/primary_text_disable_only_material_light.xml @@ -16,7 +16,7 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_enabled="false" - android:alpha="@dimen/disabled_alpha_material" - android:color="@color/bright_foreground_material_light"/> + android:alpha="@dimen/disabled_alpha_material_light" + android:color="@color/bright_foreground_material_light"/> <item android:color="@color/bright_foreground_material_light"/> </selector> diff --git a/core/res/res/color/primary_text_material_dark.xml b/core/res/res/color/primary_text_material_dark.xml index 6ad837b..690f0ec 100644 --- a/core/res/res/color/primary_text_material_dark.xml +++ b/core/res/res/color/primary_text_material_dark.xml @@ -16,7 +16,7 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_enabled="false" - android:alpha="@dimen/disabled_alpha_material" - android:color="@color/primary_text_default_material_dark"/> + android:alpha="@dimen/disabled_alpha_material_dark" + android:color="@color/primary_text_default_material_dark"/> <item android:color="@color/primary_text_default_material_dark"/> </selector> diff --git a/core/res/res/color/primary_text_material_light.xml b/core/res/res/color/primary_text_material_light.xml index 4c19e12..634ad94 100644 --- a/core/res/res/color/primary_text_material_light.xml +++ b/core/res/res/color/primary_text_material_light.xml @@ -16,7 +16,7 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_enabled="false" - android:alpha="@dimen/disabled_alpha_material" - android:color="@color/primary_text_default_material_light"/> + android:alpha="@dimen/disabled_alpha_material_light" + android:color="@color/primary_text_default_material_light"/> <item android:color="@color/primary_text_default_material_light"/> </selector> diff --git a/core/res/res/color/secondary_text_material_dark.xml b/core/res/res/color/secondary_text_material_dark.xml new file mode 100644 index 0000000..5bddf96 --- /dev/null +++ b/core/res/res/color/secondary_text_material_dark.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:alpha="@dimen/disabled_alpha_material_dark" + android:color="@color/secondary_text_default_material_dark"/> + <item android:color="@color/secondary_text_default_material_dark"/> +</selector> diff --git a/core/res/res/color/secondary_text_material_light.xml b/core/res/res/color/secondary_text_material_light.xml new file mode 100644 index 0000000..3c354cb --- /dev/null +++ b/core/res/res/color/secondary_text_material_light.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:alpha="@dimen/disabled_alpha_material_light" + android:color="@color/secondary_text_default_material_light"/> + <item android:color="@color/secondary_text_default_material_light"/> +</selector> diff --git a/core/res/res/drawable/progress_horizontal_material.xml b/core/res/res/drawable/progress_horizontal_material.xml index 7a1079c..6b64337 100644 --- a/core/res/res/drawable/progress_horizontal_material.xml +++ b/core/res/res/drawable/progress_horizontal_material.xml @@ -18,13 +18,13 @@ <item android:id="@id/background"> <nine-patch android:src="@drawable/progress_mtrl_alpha" android:tint="?attr/colorControlNormal" - android:alpha="0.5" /> + android:alpha="?attr/disabledAlpha" /> </item> <item android:id="@id/secondaryProgress"> <scale android:scaleWidth="100%"> <nine-patch android:src="@drawable/progress_mtrl_alpha" android:tint="?attr/colorControlActivated" - android:alpha="0.5" /> + android:alpha="?attr/disabledAlpha" /> </scale> </item> <item android:id="@id/progress"> diff --git a/core/res/res/layout/action_menu_item_layout.xml b/core/res/res/layout/action_menu_item_layout.xml index 04d1f7b..6961f8d 100644 --- a/core/res/res/layout/action_menu_item_layout.xml +++ b/core/res/res/layout/action_menu_item_layout.xml @@ -25,5 +25,4 @@ android:paddingStart="8dip" android:paddingEnd="8dip" android:textAppearance="?attr/actionMenuTextAppearance" - android:textColor="?attr/actionMenuTextColor" - style="?android:attr/actionButtonStyle" /> + style="?attr/actionButtonStyle" /> diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml index c9292eb..d8e14a0 100644 --- a/core/res/res/values/colors_material.xml +++ b/core/res/res/values/colors_material.xml @@ -58,19 +58,14 @@ <!-- Text & foreground colors --> <eat-comment /> - <!-- Black 87% --> <color name="primary_text_default_material_light">#de000000</color> - <!-- Black 54% --> - <color name="secondary_text_material_light">#8a000000</color> - <!-- Black 54% (TODO: same as secondary?) --> - <color name="tertiary_text_material_light">#8a000000</color> - - <!-- White 87% --> - <color name="primary_text_default_material_dark">#deffffff</color> - <!-- White 38% --> - <color name="secondary_text_material_dark">#61ffffff</color> - <!-- White 38% (TODO: same as secondary?) --> - <color name="tertiary_text_material_dark">#61ffffff</color> + <color name="secondary_text_default_material_light">#8a000000</color> + + <color name="primary_text_default_material_dark">#ffffffff</color> + <color name="secondary_text_default_material_dark">#b3ffffff</color> + + <item name="disabled_alpha_material_light" format="float" type="dimen">0.26</item> + <item name="disabled_alpha_material_dark" format="float" type="dimen">0.30</item> <!-- Primary & accent colors --> <eat-comment /> diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml index f5c9299..5f7f0ed 100644 --- a/core/res/res/values/dimens_material.xml +++ b/core/res/res/values/dimens_material.xml @@ -77,8 +77,5 @@ <!-- Default rounded corner for controls --> <dimen name="control_corner_material">2dp</dimen> - <!-- Default alpha value for disabled elements. --> - <item name="disabled_alpha_material" format="float" type="dimen">0.26</item> - - <dimen name="alert_dialog_padding_material">12dp</dimen> + <dimen name="alert_dialog_padding_material">18dp</dimen> </resources> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index 4c59f73..bd24f3e 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -87,4 +87,6 @@ <item type="id" name="transitionPosition" /> <item type="id" name="transitionTransform" /> <item type="id" name="parentMatrix" /> + <item type="id" name="statusBarBackground" /> + <item type="id" name="navigationBarBackground" /> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index d452739..5e76a87 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2288,6 +2288,8 @@ <public-padding type="id" name="l_resource_pad" end="0x01020040" /> <public type="id" name="mask" /> + <public type="id" name="statusBarBackground" /> + <public type="id" name="navigationBarBackground" /> <public-padding type="style" name="l_resource_pad" end="0x01030200" /> diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml index 89fac13..2296a12 100644 --- a/core/res/res/values/themes_material.xml +++ b/core/res/res/values/themes_material.xml @@ -46,7 +46,7 @@ please see themes_device_defaults.xml. <item name="colorForegroundInverse">@color/bright_foreground_material_light</item> <item name="colorBackground">@color/background_material_dark</item> <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item> - <item name="disabledAlpha">@dimen/disabled_alpha_material</item> + <item name="disabledAlpha">@dimen/disabled_alpha_material_dark</item> <item name="backgroundDimAmount">0.6</item> <!-- Text styles --> @@ -58,8 +58,8 @@ please see themes_device_defaults.xml. <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_dark</item> <item name="textColorSecondary">@color/secondary_text_material_dark</item> <item name="textColorSecondaryInverse">@color/secondary_text_material_light</item> - <item name="textColorTertiary">@color/tertiary_text_material_dark</item> - <item name="textColorTertiaryInverse">@color/tertiary_text_material_light</item> + <item name="textColorTertiary">@color/secondary_text_material_dark</item> + <item name="textColorTertiaryInverse">@color/secondary_text_material_light</item> <item name="textColorHint">@color/hint_foreground_material_dark</item> <item name="textColorHintInverse">@color/hint_foreground_material_light</item> <item name="textColorHighlight">@color/highlighted_text_material_dark</item> @@ -387,7 +387,7 @@ please see themes_device_defaults.xml. <item name="colorForegroundInverse">@color/bright_foreground_material_dark</item> <item name="colorBackground">@color/background_material_light</item> <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item> - <item name="disabledAlpha">@dimen/disabled_alpha_material</item> + <item name="disabledAlpha">@dimen/disabled_alpha_material_light</item> <item name="backgroundDimAmount">0.6</item> <!-- Text styles --> @@ -398,8 +398,8 @@ please see themes_device_defaults.xml. <item name="textColorPrimaryInverse">@color/primary_text_material_dark</item> <item name="textColorSecondary">@color/secondary_text_material_light</item> <item name="textColorSecondaryInverse">@color/secondary_text_material_dark</item> - <item name="textColorTertiary">@color/tertiary_text_material_light</item> - <item name="textColorTertiaryInverse">@color/tertiary_text_material_dark</item> + <item name="textColorTertiary">@color/secondary_text_material_light</item> + <item name="textColorTertiaryInverse">@color/secondary_text_material_dark</item> <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_light</item> <item name="textColorPrimaryInverseDisableOnly">@color/primary_text_disable_only_material_dark</item> <item name="textColorHint">@color/hint_foreground_material_light</item> @@ -750,8 +750,8 @@ please see themes_device_defaults.xml. <item name="textColorPrimaryInverse">@color/primary_text_material_dark</item> <item name="textColorSecondary">@color/secondary_text_material_light</item> <item name="textColorSecondaryInverse">@color/secondary_text_material_dark</item> - <item name="textColorTertiary">@color/tertiary_text_material_light</item> - <item name="textColorTertiaryInverse">@color/tertiary_text_material_dark</item> + <item name="textColorTertiary">@color/secondary_text_material_light</item> + <item name="textColorTertiaryInverse">@color/secondary_text_material_dark</item> <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_light</item> <item name="textColorPrimaryInverseDisableOnly">@color/primary_text_disable_only_material_dark</item> <item name="textColorHint">@color/hint_foreground_material_light</item> @@ -790,8 +790,8 @@ please see themes_device_defaults.xml. <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_dark</item> <item name="textColorSecondary">@color/secondary_text_material_dark</item> <item name="textColorSecondaryInverse">@color/secondary_text_material_light</item> - <item name="textColorTertiary">@color/tertiary_text_material_dark</item> - <item name="textColorTertiaryInverse">@color/tertiary_text_material_light</item> + <item name="textColorTertiary">@color/secondary_text_material_dark</item> + <item name="textColorTertiaryInverse">@color/secondary_text_material_light</item> <item name="textColorHint">@color/hint_foreground_material_dark</item> <item name="textColorHintInverse">@color/hint_foreground_material_light</item> <item name="textColorHighlight">@color/highlighted_text_material_dark</item> diff --git a/docs/html/guide/topics/admin/device-admin.jd b/docs/html/guide/topics/admin/device-admin.jd index ee6b814..bed4b4d 100644 --- a/docs/html/guide/topics/admin/device-admin.jd +++ b/docs/html/guide/topics/admin/device-admin.jd @@ -28,12 +28,6 @@ page.tags=devicepolicymanager,policy,security <li>{@link android.app.admin.DevicePolicyManager}</li> <li>{@link android.app.admin.DeviceAdminInfo}</li> </ol> - <h2>Related samples</h2> - <ol> - <li><a -href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html"> -DeviceAdminSample</a></li> -</ol> </div> </div> @@ -232,18 +226,12 @@ Administration API lets you do the following:</p> <ul> <h2 id="sample">Sample Application</h2> -<p>The examples used in this document are based on the <a -href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html"> -Device Administration API -sample</a>, which is included in the SDK samples. For information on downloading and -installing the SDK samples, see <a -href="{@docRoot}resources/samples/get.html"> -Getting the Samples</a>. Here is the <a -href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html"> -complete code</a> for -the sample. </p> -<p>The -sample application offers a demo of device admin features. It presents users +<p>The examples used in this document are based on the Device Administration API +sample, which is included in the SDK samples (available through the +Android SDK Manager) and located on your system as +<code><sdk_root>/ApiDemos/app/src/main/java/com/example/android/apis/app/DeviceAdminSample.java</code>.</p> + +<p>The sample application offers a demo of device admin features. It presents users with a user interface that lets them enable the device admin application. Once they've enabled the application, they can use the buttons in the user interface to do the following:</p> @@ -676,7 +664,8 @@ mDPM.setMaximumTimeToLock(mDeviceAdminSample, timeMs); <p>You can also programmatically tell the device to lock immediately:</p> <pre> DevicePolicyManager mDPM; -mDPM.lockNow();</pre> +mDPM.lockNow(); +</pre> @@ -692,12 +681,12 @@ wiped after a specific number of failed password attempts.</p> <pre> DevicePolicyManager mDPM; mDPM.wipeData(0);</pre> -<p>The {@link android.app.admin.DevicePolicyManager#wipeData wipeData()} method takes as its parameter a bit mask of -additional options. Currently the value must be 0. </p> +<p>The {@link android.app.admin.DevicePolicyManager#wipeData wipeData()} method takes as its + parameter a bit mask of additional options. Currently the value must be 0. </p> <h4>Disable camera</h4> <p>Beginning with Android 4.0, you can disable the camera. Note that this doesn't have to be a permanent disabling. The camera can be enabled/disabled dynamically based on context, time, and so on. </p> -<p>You control whether the camera is disabled by using the +<p>You control whether the camera is disabled by using the {@link android.app.admin.DevicePolicyManager#setCameraDisabled(android.content.ComponentName, boolean) setCameraDisabled()} method. For example, this snippet sets the camera to be enabled or disabled based on a checkbox setting:</p> <pre>private CheckBoxPreference mDisableCameraCheckbox; @@ -708,8 +697,8 @@ mDPM.setCameraDisabled(mDeviceAdminSample, mDisableCameraCheckbox.isChecked());< </pre> -<h4 id=storage">Storage encryption</h4> -<p>Beginning with Android 3.0, you can use the +<h4 id="storage">Storage encryption</h4> +<p>Beginning with Android 3.0, you can use the {@link android.app.admin.DevicePolicyManager#setStorageEncryption(android.content.ComponentName,boolean) setStorageEncryption()} method to set a policy requiring encryption of the storage area, where supported.</p> @@ -722,5 +711,5 @@ ComponentName mDeviceAdminSample; mDPM.setStorageEncryption(mDeviceAdminSample, true); </pre> <p> -See the <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html"> Device Administration API sample</a> for a complete -example of how to enable storage encryption.</p> +See the Device Administration API sample for a complete example of how to enable storage encryption. +</p>
\ No newline at end of file diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java index 063ac09..cd919a6 100644 --- a/graphics/java/android/graphics/drawable/Ripple.java +++ b/graphics/java/android/graphics/drawable/Ripple.java @@ -105,6 +105,9 @@ class Ripple { /** Whether we have an explicit maximum radius. */ private boolean mHasMaxRadius; + /** Whether we were canceled externally and should avoid self-removal. */ + private boolean mCanceled; + /** * Creates a new ripple. */ @@ -295,6 +298,8 @@ class Ripple { * Starts the enter animation. */ public void enter() { + cancel(); + final int radiusDuration = (int) (1000 * Math.sqrt(mOuterRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensity) + 0.5); @@ -332,7 +337,8 @@ class Ripple { * Starts the exit animation. */ public void exit() { - cancelSoftwareAnimations(); + cancel(); + final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius); final float remaining; if (mAnimRadius != null && mAnimRadius.isRunning()) { @@ -399,9 +405,15 @@ class Ripple { invalidateSelf(); } + /** + * Jump all animations to their end state. The caller is responsible for + * removing the ripple from the list of animating ripples. + */ public void jump() { + mCanceled = true; endSoftwareAnimations(); endHardwareAnimations(); + mCanceled = false; } private void endSoftwareAnimations() { @@ -436,6 +448,8 @@ class Ripple { mPendingAnimations.clear(); removeSelf(); } + + mHardwareAnimating = false; } private Paint getTempPaint() { @@ -479,11 +493,14 @@ class Ripple { } /** - * Cancel all animations. + * Cancels all animations. The caller is responsible for removing + * the ripple from the list of animating ripples. */ public void cancel() { + mCanceled = true; cancelSoftwareAnimations(); cancelHardwareAnimations(true); + mCanceled = false; } private void cancelSoftwareAnimations() { @@ -517,13 +534,16 @@ class Ripple { if (cancelPending && !mPendingAnimations.isEmpty()) { mPendingAnimations.clear(); - removeSelf(); } + + mHardwareAnimating = false; } private void removeSelf() { // The owner will invalidate itself. - mOwner.removeRipple(this); + if (!mCanceled) { + mOwner.removeRipple(this); + } } private void invalidateSelf() { diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java index 49862bc..40a5e18 100644 --- a/graphics/java/android/graphics/drawable/RippleBackground.java +++ b/graphics/java/android/graphics/drawable/RippleBackground.java @@ -42,8 +42,9 @@ class RippleBackground { private static final float GLOBAL_SPEED = 1.0f; private static final float WAVE_TOUCH_DOWN_ACCELERATION = 1024.0f * GLOBAL_SPEED; private static final float WAVE_OPACITY_DECAY_VELOCITY = 3.0f / GLOBAL_SPEED; - private static final float WAVE_OUTER_OPACITY_VELOCITY_MAX = 4.5f * GLOBAL_SPEED; - private static final float WAVE_OUTER_OPACITY_VELOCITY_MIN = 1.5f * GLOBAL_SPEED; + private static final float WAVE_OUTER_OPACITY_EXIT_VELOCITY_MAX = 4.5f * GLOBAL_SPEED; + private static final float WAVE_OUTER_OPACITY_EXIT_VELOCITY_MIN = 1.5f * GLOBAL_SPEED; + private static final float WAVE_OUTER_OPACITY_ENTER_VELOCITY = 10.0f * GLOBAL_SPEED; private static final float WAVE_OUTER_SIZE_INFLUENCE_MAX = 200f; private static final float WAVE_OUTER_SIZE_INFLUENCE_MIN = 40f; @@ -106,6 +107,9 @@ class RippleBackground { /** Whether we have an explicit maximum radius. */ private boolean mHasMaxRadius; + /** Whether we were canceled externally and should avoid self-removal. */ + private boolean mCanceled; + /** * Creates a new ripple. */ @@ -283,9 +287,11 @@ class RippleBackground { * Starts the enter animation. */ public void enter() { + cancel(); + final int radiusDuration = (int) (1000 * Math.sqrt(mOuterRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensity) + 0.5); - final int outerDuration = (int) (1000 * 1.0f / WAVE_OUTER_OPACITY_VELOCITY_MIN); + final int outerDuration = (int) (1000 * 1.0f / WAVE_OUTER_OPACITY_ENTER_VELOCITY); final ObjectAnimator cX = ObjectAnimator.ofFloat(this, "xGravity", 1); cX.setAutoCancel(true); @@ -320,7 +326,7 @@ class RippleBackground { * Starts the exit animation. */ public void exit() { - cancelSoftwareAnimations(); + cancel(); // Scale the outer max opacity and opacity velocity based // on the size of the outer radius. @@ -328,8 +334,8 @@ class RippleBackground { final float outerSizeInfluence = MathUtils.constrain( (mOuterRadius - WAVE_OUTER_SIZE_INFLUENCE_MIN * mDensity) / (WAVE_OUTER_SIZE_INFLUENCE_MAX * mDensity), 0, 1); - final float outerOpacityVelocity = MathUtils.lerp(WAVE_OUTER_OPACITY_VELOCITY_MIN, - WAVE_OUTER_OPACITY_VELOCITY_MAX, outerSizeInfluence); + final float outerOpacityVelocity = MathUtils.lerp(WAVE_OUTER_OPACITY_EXIT_VELOCITY_MIN, + WAVE_OUTER_OPACITY_EXIT_VELOCITY_MAX, outerSizeInfluence); // Determine at what time the inner and outer opacity intersect. // inner(t) = mOpacity - t * WAVE_OPACITY_DECAY_VELOCITY / 1000 @@ -403,9 +409,15 @@ class RippleBackground { invalidateSelf(); } + /** + * Jump all animations to their end state. The caller is responsible for + * removing the ripple from the list of animating ripples. + */ public void jump() { + mCanceled = true; endSoftwareAnimations(); endHardwareAnimations(); + mCanceled = false; } private void endSoftwareAnimations() { @@ -436,6 +448,8 @@ class RippleBackground { mPendingAnimations.clear(); removeSelf(); } + + mHardwareAnimating = false; } private Paint getTempPaint() { @@ -508,11 +522,14 @@ class RippleBackground { } /** - * Cancel all animations. + * Cancel all animations. The caller is responsible for removing + * the ripple from the list of animating ripples. */ public void cancel() { + mCanceled = true; cancelSoftwareAnimations(); cancelHardwareAnimations(true); + mCanceled = false; } private void cancelSoftwareAnimations() { @@ -543,13 +560,16 @@ class RippleBackground { if (cancelPending && !mPendingAnimations.isEmpty()) { mPendingAnimations.clear(); - removeSelf(); } + + mHardwareAnimating = false; } private void removeSelf() { // The owner will invalidate itself. - mOwner.removeBackground(this); + if (!mCanceled) { + mOwner.removeBackground(this); + } } private void invalidateSelf() { diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index 7402bdb..b90fd81 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -140,8 +140,8 @@ public class RippleDrawable extends LayerDrawable { * Lazily-created array of actively animating ripples. Inactive ripples are * pruned during draw(). The locations of these will not change. */ - private Ripple[] mAnimatingRipples; - private int mAnimatingRipplesCount = 0; + private Ripple[] mExitingRipples; + private int mExitingRipplesCount = 0; /** Paint used to control appearance of ripples. */ private Paint mRipplePaint; @@ -156,12 +156,6 @@ public class RippleDrawable extends LayerDrawable { private boolean mOverrideBounds; /** - * Whether hotspots are being cleared. Used to prevent re-entry by - * animation finish listeners. - */ - private boolean mClearingHotspots; - - /** * Constructor used for drawable inflation. */ RippleDrawable() { @@ -209,19 +203,21 @@ public class RippleDrawable extends LayerDrawable { mBackground.jump(); } - mClearingHotspots = true; - final int count = mAnimatingRipplesCount; - final Ripple[] ripples = mAnimatingRipples; + cancelExitingRipples(); + invalidateSelf(); + } + + private void cancelExitingRipples() { + final int count = mExitingRipplesCount; + final Ripple[] ripples = mExitingRipples; for (int i = 0; i < count; i++) { - ripples[i].jump(); + ripples[i].cancel(); } + if (ripples != null) { Arrays.fill(ripples, 0, count, null); } - mAnimatingRipplesCount = 0; - mClearingHotspots = false; - - invalidateSelf(); + mExitingRipplesCount = 0; } @Override @@ -287,9 +283,9 @@ public class RippleDrawable extends LayerDrawable { if (mRippleActive != active) { mRippleActive = active; if (active) { - activateRipple(); + tryRippleEnter(); } else { - removeRipple(); + tryRippleExit(); } } } @@ -298,9 +294,9 @@ public class RippleDrawable extends LayerDrawable { if (mBackgroundActive != active) { mBackgroundActive = active; if (active) { - activateBackground(); + tryBackgroundEnter(); } else { - removeBackground(); + tryBackgroundExit(); } } } @@ -327,11 +323,11 @@ public class RippleDrawable extends LayerDrawable { // If we just became visible, ensure the background and ripple // visibilities are consistent with their internal states. if (mRippleActive) { - activateRipple(); + tryRippleEnter(); } if (mBackgroundActive) { - activateBackground(); + tryBackgroundEnter(); } } @@ -491,7 +487,7 @@ public class RippleDrawable extends LayerDrawable { /** * Creates an active hotspot at the specified location. */ - private void activateBackground() { + private void tryBackgroundEnter() { if (mBackground == null) { final float x; final float y; @@ -511,7 +507,7 @@ public class RippleDrawable extends LayerDrawable { mBackground.enter(); } - private void removeBackground() { + private void tryBackgroundExit() { if (mBackground != null) { // Don't null out the background, we need it to draw! mBackground.exit(); @@ -519,10 +515,11 @@ public class RippleDrawable extends LayerDrawable { } /** - * Creates an active hotspot at the specified location. + * Attempts to start an enter animation for the active hotspot. Fails if + * there are too many animating ripples. */ - private void activateRipple() { - if (mAnimatingRipplesCount >= MAX_RIPPLES) { + private void tryRippleEnter() { + if (mExitingRipplesCount >= MAX_RIPPLES) { // This should never happen unless the user is tapping like a maniac // or there is a bug that's preventing ripples from being removed. return; @@ -545,29 +542,27 @@ public class RippleDrawable extends LayerDrawable { final int color = mState.mColor.getColorForState(getState(), Color.TRANSPARENT); mRipple.setup(mState.mMaxRadius, color, mDensity); mRipple.enter(); - - if (mAnimatingRipples == null) { - mAnimatingRipples = new Ripple[MAX_RIPPLES]; - } - mAnimatingRipples[mAnimatingRipplesCount++] = mRipple; } - @Override - public void invalidateSelf() { - // Don't invalidate when we're clearing hotspots. We'll handle that - // manually when we're done. - if (!mClearingHotspots) { - super.invalidateSelf(); - } - } - - private void removeRipple() { + /** + * Attempts to start an exit animation for the active hotspot. Fails if + * there is no active hotspot. + */ + private void tryRippleExit() { if (mRipple != null) { + if (mExitingRipples == null) { + mExitingRipples = new Ripple[MAX_RIPPLES]; + } + mExitingRipples[mExitingRipplesCount++] = mRipple; mRipple.exit(); mRipple = null; } } + /** + * Cancels and removes the active ripple, all exiting ripples, and the + * background. Nothing will be drawn after this method is called. + */ private void clearHotspots() { if (mRipple != null) { mRipple.cancel(); @@ -579,18 +574,7 @@ public class RippleDrawable extends LayerDrawable { mBackground = null; } - mClearingHotspots = true; - final int count = mAnimatingRipplesCount; - final Ripple[] ripples = mAnimatingRipples; - for (int i = 0; i < count; i++) { - ripples[i].cancel(); - } - if (ripples != null) { - Arrays.fill(ripples, 0, count, null); - } - mAnimatingRipplesCount = 0; - mClearingHotspots = false; - + cancelExitingRipples(); invalidateSelf(); } @@ -612,8 +596,8 @@ public class RippleDrawable extends LayerDrawable { * Notifies all the animating ripples that the hotspot bounds have changed. */ private void onHotspotBoundsChanged() { - final int count = mAnimatingRipplesCount; - final Ripple[] ripples = mAnimatingRipples; + final int count = mExitingRipplesCount; + final Ripple[] ripples = mExitingRipples; for (int i = 0; i < count; i++) { ripples[i].onHotspotBoundsChanged(); } @@ -683,22 +667,21 @@ public class RippleDrawable extends LayerDrawable { } /** - * Removes a ripple from the animating ripple list. + * Removes a ripple from the exiting ripple list. * * @param ripple the ripple to remove */ void removeRipple(Ripple ripple) { - if (!mClearingHotspots) { - // Ripple ripple ripple ripple. Ripple ripple. - final Ripple[] ripples = mAnimatingRipples; - final int count = mAnimatingRipplesCount; - final int index = getRippleIndex(ripple); - if (index >= 0) { - System.arraycopy(ripples, index + 1, ripples, index, count - (index + 1)); - ripples[count - 1] = null; - mAnimatingRipplesCount--; - invalidateSelf(); - } + // Ripple ripple ripple ripple. Ripple ripple. + final Ripple[] ripples = mExitingRipples; + final int count = mExitingRipplesCount; + final int index = getRippleIndex(ripple); + if (index >= 0) { + System.arraycopy(ripples, index + 1, ripples, index, count - (index + 1)); + ripples[count - 1] = null; + mExitingRipplesCount--; + + invalidateSelf(); } } @@ -710,8 +693,8 @@ public class RippleDrawable extends LayerDrawable { } private int getRippleIndex(Ripple ripple) { - final Ripple[] ripples = mAnimatingRipples; - final int count = mAnimatingRipplesCount; + final Ripple[] ripples = mExitingRipples; + final int count = mExitingRipplesCount; for (int i = 0; i < count; i++) { if (ripples[i] == ripple) { return i; @@ -727,7 +710,7 @@ public class RippleDrawable extends LayerDrawable { // We don't need a layer if we don't expect to draw any ripples, we have // an explicit mask, or if the non-mask content is all opaque. boolean needsLayer = false; - if ((mAnimatingRipplesCount > 0 || mBackground != null) && mMask == null) { + if ((mExitingRipplesCount > 0 || mBackground != null) && mMask == null) { for (int i = 0; i < count; i++) { if (array[i].mId != R.id.mask && array[i].mDrawable.getOpacity() != PixelFormat.OPAQUE) { @@ -831,10 +814,17 @@ public class RippleDrawable extends LayerDrawable { int restoreTranslate = -1; // Draw ripples and update the animating ripples array. - final int count = mAnimatingRipplesCount; - final Ripple[] ripples = mAnimatingRipples; - for (int i = 0; i < count; i++) { - final Ripple ripple = ripples[i]; + final int count = mExitingRipplesCount; + final Ripple[] ripples = mExitingRipples; + for (int i = 0; i <= count; i++) { + final Ripple ripple; + if (i < count) { + ripple = ripples[i]; + } else if (mRipple != null) { + ripple = mRipple; + } else { + continue; + } // If we're masking the ripple layer, make sure we have a layer // first. This will merge SRC_OVER (directly) onto the canvas. @@ -898,8 +888,9 @@ public class RippleDrawable extends LayerDrawable { final int cX = (int) mHotspotBounds.exactCenterX(); final int cY = (int) mHotspotBounds.exactCenterY(); final Rect rippleBounds = mTempRect; - final Ripple[] activeRipples = mAnimatingRipples; - final int N = mAnimatingRipplesCount; + + final Ripple[] activeRipples = mExitingRipples; + final int N = mExitingRipplesCount; for (int i = 0; i < N; i++) { activeRipples[i].getBounds(rippleBounds); rippleBounds.offset(cX, cY); diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 6ac49ee..0db8c77 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -331,6 +331,36 @@ public class KeyStore { } } + public boolean resetUid(int uid) { + try { + mError = mBinder.reset_uid(uid); + return mError == NO_ERROR; + } catch (RemoteException e) { + Log.w(TAG, "Cannot connect to keystore", e); + return false; + } + } + + public boolean syncUid(int sourceUid, int targetUid) { + try { + mError = mBinder.sync_uid(sourceUid, targetUid); + return mError == NO_ERROR; + } catch (RemoteException e) { + Log.w(TAG, "Cannot connect to keystore", e); + return false; + } + } + + public boolean passwordUid(String password, int uid) { + try { + mError = mBinder.password_uid(password, uid); + return mError == NO_ERROR; + } catch (RemoteException e) { + Log.w(TAG, "Cannot connect to keystore", e); + return false; + } + } + public int getLastError() { return mError; } diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk index a10c387..5808d20 100644 --- a/libs/androidfw/tests/Android.mk +++ b/libs/androidfw/tests/Android.mk @@ -26,6 +26,7 @@ testFiles := \ Idmap_test.cpp \ ResTable_test.cpp \ Split_test.cpp \ + Theme_test.cpp \ TypeWrappers_test.cpp \ ZipUtils_test.cpp diff --git a/libs/androidfw/tests/BackupData_test.cpp b/libs/androidfw/tests/BackupData_test.cpp index 17f91ca..92af7fe 100644 --- a/libs/androidfw/tests/BackupData_test.cpp +++ b/libs/androidfw/tests/BackupData_test.cpp @@ -45,7 +45,7 @@ namespace android { class BackupDataTest : public testing::Test { protected: char* m_external_storage; - char* m_filename; + String8 mFilename; String8 mKey1; String8 mKey2; String8 mKey3; @@ -53,15 +53,13 @@ protected: virtual void SetUp() { m_external_storage = getenv("EXTERNAL_STORAGE"); + mFilename.append(m_external_storage); + mFilename.append(TEST_FILENAME); - const int totalLen = strlen(m_external_storage) + strlen(TEST_FILENAME) + 1; - m_filename = new char[totalLen]; - snprintf(m_filename, totalLen, "%s%s", m_external_storage, TEST_FILENAME); - - ::unlink(m_filename); - int fd = ::open(m_filename, O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + ::unlink(mFilename.string()); + int fd = ::open(mFilename.string(), O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (fd < 0) { - FAIL() << "Couldn't create " << m_filename << " for writing"; + FAIL() << "Couldn't create " << mFilename.string() << " for writing"; } mKey1 = String8(KEY1); mKey2 = String8(KEY2); @@ -74,7 +72,7 @@ protected: }; TEST_F(BackupDataTest, WriteAndReadSingle) { - int fd = ::open(m_filename, O_WRONLY); + int fd = ::open(mFilename.string(), O_WRONLY); BackupDataWriter* writer = new BackupDataWriter(fd); EXPECT_EQ(NO_ERROR, writer->WriteEntityHeader(mKey1, sizeof(DATA1))) @@ -83,7 +81,7 @@ TEST_F(BackupDataTest, WriteAndReadSingle) { << "WriteEntityData returned an error"; ::close(fd); - fd = ::open(m_filename, O_RDONLY); + fd = ::open(mFilename.string(), O_RDONLY); BackupDataReader* reader = new BackupDataReader(fd); EXPECT_EQ(NO_ERROR, reader->Status()) << "Reader ctor failed"; @@ -116,7 +114,7 @@ TEST_F(BackupDataTest, WriteAndReadSingle) { } TEST_F(BackupDataTest, WriteAndReadMultiple) { - int fd = ::open(m_filename, O_WRONLY); + int fd = ::open(mFilename.string(), O_WRONLY); BackupDataWriter* writer = new BackupDataWriter(fd); writer->WriteEntityHeader(mKey1, sizeof(DATA1)); writer->WriteEntityData(DATA1, sizeof(DATA1)); @@ -124,7 +122,7 @@ TEST_F(BackupDataTest, WriteAndReadMultiple) { writer->WriteEntityData(DATA2, sizeof(DATA2)); ::close(fd); - fd = ::open(m_filename, O_RDONLY); + fd = ::open(mFilename.string(), O_RDONLY); BackupDataReader* reader = new BackupDataReader(fd); bool done; @@ -164,7 +162,7 @@ TEST_F(BackupDataTest, WriteAndReadMultiple) { } TEST_F(BackupDataTest, SkipEntity) { - int fd = ::open(m_filename, O_WRONLY); + int fd = ::open(mFilename.string(), O_WRONLY); BackupDataWriter* writer = new BackupDataWriter(fd); writer->WriteEntityHeader(mKey1, sizeof(DATA1)); writer->WriteEntityData(DATA1, sizeof(DATA1)); @@ -174,7 +172,7 @@ TEST_F(BackupDataTest, SkipEntity) { writer->WriteEntityData(DATA3, sizeof(DATA3)); ::close(fd); - fd = ::open(m_filename, O_RDONLY); + fd = ::open(mFilename.string(), O_RDONLY); BackupDataReader* reader = new BackupDataReader(fd); bool done; @@ -219,14 +217,14 @@ TEST_F(BackupDataTest, SkipEntity) { } TEST_F(BackupDataTest, DeleteEntity) { - int fd = ::open(m_filename, O_WRONLY); + int fd = ::open(mFilename.string(), O_WRONLY); BackupDataWriter* writer = new BackupDataWriter(fd); writer->WriteEntityHeader(mKey1, sizeof(DATA1)); writer->WriteEntityData(DATA1, sizeof(DATA1)); writer->WriteEntityHeader(mKey2, -1); ::close(fd); - fd = ::open(m_filename, O_RDONLY); + fd = ::open(mFilename.string(), O_RDONLY); BackupDataReader* reader = new BackupDataReader(fd); bool done; @@ -258,7 +256,7 @@ TEST_F(BackupDataTest, DeleteEntity) { } TEST_F(BackupDataTest, EneityAfterDelete) { - int fd = ::open(m_filename, O_WRONLY); + int fd = ::open(mFilename.string(), O_WRONLY); BackupDataWriter* writer = new BackupDataWriter(fd); writer->WriteEntityHeader(mKey1, sizeof(DATA1)); writer->WriteEntityData(DATA1, sizeof(DATA1)); @@ -267,7 +265,7 @@ TEST_F(BackupDataTest, EneityAfterDelete) { writer->WriteEntityData(DATA3, sizeof(DATA3)); ::close(fd); - fd = ::open(m_filename, O_RDONLY); + fd = ::open(mFilename.string(), O_RDONLY); BackupDataReader* reader = new BackupDataReader(fd); bool done; @@ -319,7 +317,7 @@ TEST_F(BackupDataTest, EneityAfterDelete) { } TEST_F(BackupDataTest, OnlyDeleteEntities) { - int fd = ::open(m_filename, O_WRONLY); + int fd = ::open(mFilename.string(), O_WRONLY); BackupDataWriter* writer = new BackupDataWriter(fd); writer->WriteEntityHeader(mKey1, -1); writer->WriteEntityHeader(mKey2, -1); @@ -327,7 +325,7 @@ TEST_F(BackupDataTest, OnlyDeleteEntities) { writer->WriteEntityHeader(mKey4, -1); ::close(fd); - fd = ::open(m_filename, O_RDONLY); + fd = ::open(mFilename.string(), O_RDONLY); BackupDataReader* reader = new BackupDataReader(fd); bool done; @@ -387,13 +385,13 @@ TEST_F(BackupDataTest, OnlyDeleteEntities) { } TEST_F(BackupDataTest, ReadDeletedEntityData) { - int fd = ::open(m_filename, O_WRONLY); + int fd = ::open(mFilename.string(), O_WRONLY); BackupDataWriter* writer = new BackupDataWriter(fd); writer->WriteEntityHeader(mKey1, -1); writer->WriteEntityHeader(mKey2, -1); ::close(fd); - fd = ::open(m_filename, O_RDONLY); + fd = ::open(mFilename.string(), O_RDONLY); BackupDataReader* reader = new BackupDataReader(fd); bool done; @@ -431,6 +429,7 @@ TEST_F(BackupDataTest, ReadDeletedEntityData) { EXPECT_EQ(-1, (int) dataSize) << "not recognizing deletion on second entity"; + delete[] dataBytes; delete writer; delete reader; } diff --git a/libs/androidfw/tests/ObbFile_test.cpp b/libs/androidfw/tests/ObbFile_test.cpp index 7a4dd13..1151121 100644 --- a/libs/androidfw/tests/ObbFile_test.cpp +++ b/libs/androidfw/tests/ObbFile_test.cpp @@ -34,20 +34,18 @@ namespace android { class ObbFileTest : public testing::Test { protected: sp<ObbFile> mObbFile; - char* mExternalStorage; - char* mFileName; + String8 mFileName; virtual void SetUp() { mObbFile = new ObbFile(); - mExternalStorage = getenv("EXTERNAL_STORAGE"); + char* externalStorage = getenv("EXTERNAL_STORAGE"); - const int totalLen = strlen(mExternalStorage) + strlen(TEST_FILENAME) + 1; - mFileName = new char[totalLen]; - snprintf(mFileName, totalLen, "%s%s", mExternalStorage, TEST_FILENAME); + mFileName.append(externalStorage); + mFileName.append(TEST_FILENAME); - int fd = ::open(mFileName, O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + int fd = ::open(mFileName.string(), O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (fd < 0) { - FAIL() << "Couldn't create " << mFileName << " for tests"; + FAIL() << "Couldn't create " << mFileName.string() << " for tests"; } } @@ -71,12 +69,12 @@ TEST_F(ObbFileTest, WriteThenRead) { EXPECT_TRUE(mObbFile->setSalt(salt, SALT_SIZE)) << "Salt should be successfully set"; - EXPECT_TRUE(mObbFile->writeTo(mFileName)) + EXPECT_TRUE(mObbFile->writeTo(mFileName.string())) << "couldn't write to fake .obb file"; mObbFile = new ObbFile(); - EXPECT_TRUE(mObbFile->readFrom(mFileName)) + EXPECT_TRUE(mObbFile->readFrom(mFileName.string())) << "couldn't read from fake .obb file"; EXPECT_EQ(versionNum, mObbFile->getVersion()) diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp new file mode 100644 index 0000000..4d07130 --- /dev/null +++ b/libs/androidfw/tests/Theme_test.cpp @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#include <androidfw/ResourceTypes.h> + +#include <utils/String8.h> +#include <utils/String16.h> +#include "TestHelpers.h" +#include "data/system/R.h" +#include "data/app/R.h" + +#include <gtest/gtest.h> + +using namespace android; + +namespace { + +#include "data/system/system_arsc.h" +#include "data/app/app_arsc.h" + +enum { MAY_NOT_BE_BAG = false }; + +/** + * TODO(adamlesinski): Enable when fixed. + */ +TEST(ThemeTest, DISABLED_shouldCopyThemeFromDifferentResTable) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(system_arsc, system_arsc_len)); + ASSERT_EQ(NO_ERROR, table.add(app_arsc, app_arsc_len)); + + ResTable::Theme theme1(table); + ASSERT_EQ(NO_ERROR, theme1.applyStyle(app::R::style::Theme_One)); + Res_value val; + ASSERT_GE(theme1.getAttribute(android::R::attr::background, &val), 0); + ASSERT_EQ(Res_value::TYPE_INT_COLOR_RGB8, val.dataType); + ASSERT_EQ(uint32_t(0xffff0000), val.data); + ASSERT_GE(theme1.getAttribute(app::R::attr::number, &val), 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(1), val.data); + + ResTable table2; + ASSERT_EQ(NO_ERROR, table2.add(system_arsc, system_arsc_len)); + ASSERT_EQ(NO_ERROR, table2.add(app_arsc, app_arsc_len)); + + ResTable::Theme theme2(table2); + ASSERT_EQ(NO_ERROR, theme2.setTo(theme1)); + ASSERT_GE(theme2.getAttribute(android::R::attr::background, &val), 0); + ASSERT_EQ(Res_value::TYPE_INT_COLOR_RGB8, val.dataType); + ASSERT_EQ(uint32_t(0xffff0000), val.data); + ASSERT_GE(theme2.getAttribute(app::R::attr::number, &val), 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(1), val.data); +} + +} diff --git a/packages/DocumentsUI/res/drawable/grid_protect_background.xml b/libs/androidfw/tests/data/app/AndroidManifest.xml index 2e7aadd..bfa3a39 100644 --- a/packages/DocumentsUI/res/drawable/grid_protect_background.xml +++ b/libs/androidfw/tests/data/app/AndroidManifest.xml @@ -14,11 +14,6 @@ limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_enabled="false"> - <color android:color="#88000000" /> - </item> - <item> - <color android:color="#88252525" /> - </item> -</selector> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.app"> +</manifest> diff --git a/libs/androidfw/tests/data/app/R.h b/libs/androidfw/tests/data/app/R.h new file mode 100644 index 0000000..780a116 --- /dev/null +++ b/libs/androidfw/tests/data/app/R.h @@ -0,0 +1,22 @@ +#ifndef __APP_R_H +#define __APP_R_H + +namespace app { +namespace R { + +namespace attr { + enum { + number = 0x7f010000, // default + }; +} + +namespace style { + enum { + Theme_One = 0x7f020000, // default + }; +} + +} // namespace R +} // namespace app + +#endif // __APP_R_H diff --git a/libs/androidfw/tests/data/app/app_arsc.h b/libs/androidfw/tests/data/app/app_arsc.h new file mode 100644 index 0000000..d5d9a3b --- /dev/null +++ b/libs/androidfw/tests/data/app/app_arsc.h @@ -0,0 +1,62 @@ +unsigned char app_arsc[] = { + 0x02, 0x00, 0x0c, 0x00, 0xc4, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x9c, 0x02, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, + 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, + 0x64, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, + 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6e, 0x00, + 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, + 0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00 +}; +unsigned int app_arsc_len = 708; diff --git a/libs/androidfw/tests/data/app/build b/libs/androidfw/tests/data/app/build new file mode 100755 index 0000000..89c4641 --- /dev/null +++ b/libs/androidfw/tests/data/app/build @@ -0,0 +1,6 @@ +#!/bin/bash + +aapt package -v -I ../system/bundle.apk -M AndroidManifest.xml -S res -F bundle.apk -f && \ +unzip bundle.apk resources.arsc && \ +mv resources.arsc app.arsc && \ +xxd -i app.arsc > app_arsc.h diff --git a/libs/androidfw/tests/data/app/res/values/values.xml b/libs/androidfw/tests/data/app/res/values/values.xml new file mode 100644 index 0000000..b0ead38 --- /dev/null +++ b/libs/androidfw/tests/data/app/res/values/values.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <attr name="number" format="integer"/> + <style name="Theme.One" parent="@android:style/Theme.One"> + <item name="number">1</item> + </style> +</resources> diff --git a/libs/androidfw/tests/data/system/AndroidManifest.xml b/libs/androidfw/tests/data/system/AndroidManifest.xml new file mode 100644 index 0000000..af105ee --- /dev/null +++ b/libs/androidfw/tests/data/system/AndroidManifest.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android"> +</manifest> diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h new file mode 100644 index 0000000..7a9d3db --- /dev/null +++ b/libs/androidfw/tests/data/system/R.h @@ -0,0 +1,23 @@ +#ifndef __ANDROID_R_H +#define __ANDROID_R_H + +namespace android { +namespace R { + +namespace attr { + enum { + background = 0x01010000, // default + foreground = 0x01010001, // default + }; +} + +namespace style { + enum { + Theme_One = 0x01020000, // default + }; +} + +} // namespace R +} // namespace android + +#endif // __ANDROID_R_H diff --git a/libs/androidfw/tests/data/system/build b/libs/androidfw/tests/data/system/build new file mode 100755 index 0000000..2a3ac0b --- /dev/null +++ b/libs/androidfw/tests/data/system/build @@ -0,0 +1,6 @@ +#!/bin/bash + +aapt package -x -M AndroidManifest.xml -S res -F bundle.apk -f && \ +unzip bundle.apk resources.arsc && \ +mv resources.arsc system.arsc && \ +xxd -i system.arsc > system_arsc.h diff --git a/libs/androidfw/tests/data/system/res/values/themes.xml b/libs/androidfw/tests/data/system/res/values/themes.xml new file mode 100644 index 0000000..b29848e --- /dev/null +++ b/libs/androidfw/tests/data/system/res/values/themes.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <public name="background" type="attr" id="0x01010000"/> + <public name="foreground" type="attr" id="0x01010001"/> + <public name="Theme.One" type="style" id="0x01020000"/> + + <attr name="background" format="color|reference"/> + <attr name="foreground" format="color|reference"/> + <style name="Theme.One" parent=""> + <item name="android:background">#ff0000</item> + <item name="android:foreground">#000000</item> + </style> +</resources> diff --git a/libs/androidfw/tests/data/system/system_arsc.h b/libs/androidfw/tests/data/system/system_arsc.h new file mode 100644 index 0000000..215ecae --- /dev/null +++ b/libs/androidfw/tests/data/system/system_arsc.h @@ -0,0 +1,69 @@ +unsigned char system_arsc[] = { + 0x02, 0x00, 0x0c, 0x00, 0x18, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xf0, 0x02, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, + 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, + 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x62, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x67, 0x00, + 0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x67, 0x00, + 0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, + 0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, + 0x01, 0x02, 0x44, 0x00, 0x84, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x08, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x44, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0xff, 0xff, + 0x01, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xff +}; +unsigned int system_arsc_len = 792; diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp index 9cc83ed..7834ef8 100644 --- a/libs/hwui/AmbientShadow.cpp +++ b/libs/hwui/AmbientShadow.cpp @@ -16,6 +16,43 @@ #define LOG_TAG "OpenGLRenderer" +/** + * Extra vertices for the corner for smoother corner. + * Only for outer vertices. + * Note that we use such extra memory to avoid an extra loop. + */ +// For half circle, we could add EXTRA_VERTEX_PER_PI vertices. +// Set to 1 if we don't want to have any. +#define EXTRA_CORNER_VERTEX_PER_PI 12 + +// For the whole polygon, the sum of all the deltas b/t normals is 2 * M_PI, +// therefore, the maximum number of extra vertices will be twice bigger. +#define MAX_EXTRA_CORNER_VERTEX_NUMBER (2 * EXTRA_CORNER_VERTEX_PER_PI) + +// For each RADIANS_DIVISOR, we would allocate one more vertex b/t the normals. +#define CORNER_RADIANS_DIVISOR (M_PI / EXTRA_CORNER_VERTEX_PER_PI) + +/** + * Extra vertices for the Edge for interpolation artifacts. + * Same value for both inner and outer vertices. + */ +#define EXTRA_EDGE_VERTEX_PER_PI 50 + +#define MAX_EXTRA_EDGE_VERTEX_NUMBER (2 * EXTRA_EDGE_VERTEX_PER_PI) + +#define EDGE_RADIANS_DIVISOR (M_PI / EXTRA_EDGE_VERTEX_PER_PI) + +/** + * Other constants: + */ +// For the edge of the penumbra, the opacity is 0. +#define OUTER_OPACITY (0.0f) + +// Once the alpha difference is greater than this threshold, we will allocate extra +// edge vertices. +// If this is set to negative value, then all the edge will be tessellated. +#define ALPHA_THRESHOLD (0.1f / 255.0f) + #include <math.h> #include <utils/Log.h> #include <utils/Vector.h> @@ -23,11 +60,97 @@ #include "AmbientShadow.h" #include "ShadowTessellator.h" #include "Vertex.h" +#include "utils/MathUtils.h" namespace android { namespace uirenderer { /** + * Local utility functions. + */ +inline Vector2 getNormalFromVertices(const Vector3* vertices, int current, int next) { + // Convert from Vector3 to Vector2 first. + Vector2 currentVertex = { vertices[current].x, vertices[current].y }; + Vector2 nextVertex = { vertices[next].x, vertices[next].y }; + + return ShadowTessellator::calculateNormal(currentVertex, nextVertex); +} + +// The input z value will be converted to be non-negative inside. +// The output must be ranged from 0 to 1. +inline float getAlphaFromFactoredZ(float factoredZ) { + return 1.0 / (1 + MathUtils::max(factoredZ, 0.0f)); +} + +inline float getTransformedAlphaFromAlpha(float alpha) { + return acosf(1.0f - 2.0f * alpha); +} + +// The output is ranged from 0 to M_PI. +inline float getTransformedAlphaFromFactoredZ(float factoredZ) { + return getTransformedAlphaFromAlpha(getAlphaFromFactoredZ(factoredZ)); +} + +inline int getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2, + float divisor) { + // The formula is : + // extraNumber = floor(acos(dot(n1, n2)) / (M_PI / EXTRA_VERTEX_PER_PI)) + // The value ranges for each step are: + // dot( ) --- [-1, 1] + // acos( ) --- [0, M_PI] + // floor(...) --- [0, EXTRA_VERTEX_PER_PI] + float dotProduct = vector1.dot(vector2); + // TODO: Use look up table for the dotProduct to extraVerticesNumber + // computation, if needed. + float angle = acosf(dotProduct); + return (int) floor(angle / divisor); +} + +inline void checkOverflow(int used, int total, const char* bufferName) { + LOG_ALWAYS_FATAL_IF(used > total, "Error: %s overflow!!! used %d, total %d", + bufferName, used, total); +} + +inline int getEdgeExtraAndUpdateSpike(Vector2* currentSpike, + const Vector3& secondVertex, const Vector3& centroid) { + Vector2 secondSpike = {secondVertex.x - centroid.x, secondVertex.y - centroid.y}; + secondSpike.normalize(); + + int result = getExtraVertexNumber(secondSpike, *currentSpike, EDGE_RADIANS_DIVISOR); + *currentSpike = secondSpike; + return result; +} + +// Given the caster's vertex count, compute all the buffers size depending on +// whether or not the caster is opaque. +inline void computeBufferSize(int* totalVertexCount, int* totalIndexCount, + int* totalUmbraCount, int casterVertexCount, bool isCasterOpaque) { + // Compute the size of the vertex buffer. + int outerVertexCount = casterVertexCount * 2 + MAX_EXTRA_CORNER_VERTEX_NUMBER + + MAX_EXTRA_EDGE_VERTEX_NUMBER; + int innerVertexCount = casterVertexCount + MAX_EXTRA_EDGE_VERTEX_NUMBER; + *totalVertexCount = outerVertexCount + innerVertexCount; + + // Compute the size of the index buffer. + *totalIndexCount = 2 * outerVertexCount + 2; + + // Compute the size of the umber buffer. + // For translucent object, keep track of the umbra(inner) vertex in order to draw + // inside. We only need to store the index information. + *totalUmbraCount = 0; + if (!isCasterOpaque) { + // Add the centroid if occluder is translucent. + *totalVertexCount++; + *totalIndexCount += 2 * innerVertexCount + 1; + *totalUmbraCount = innerVertexCount; + } +} + +inline bool needsExtraForEdge(float firstAlpha, float secondAlpha) { + return abs(firstAlpha - secondAlpha) > ALPHA_THRESHOLD; +} + +/** * Calculate the shadows as a triangle strips while alpha value as the * shadow values. * @@ -43,290 +166,198 @@ namespace uirenderer { * * @param shadowVertexBuffer Return an floating point array of (x, y, a) * triangle strips mode. + * + * An simple illustration: + * For now let's mark the outer vertex as Pi, the inner as Vi, the centroid as C. + * + * First project the occluder to the Z=0 surface. + * Then we got all the inner vertices. And we compute the normal for each edge. + * According to the normal, we generate outer vertices. E.g: We generate P1 / P4 + * as extra corner vertices to make the corner looks round and smoother. + * + * Due to the fact that the alpha is not linear interpolated along the inner + * edge, when the alpha is different, we may add extra vertices such as P2.1, P2.2, + * V0.1, V0.2 to avoid the visual artifacts. + * + * (P3) + * (P2) (P2.1) (P2.2) | ' (P4) + * (P1)' | | | | ' + * ' | | | | ' + * (P0) ------------------------------------------------(P5) + * | (V0) (V0.1) (V0.2) |(V1) + * | | + * | | + * | (C) | + * | | + * | | + * | | + * | | + * (V3)-----------------------------------(V2) */ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, - const Vector3* vertices, int vertexCount, const Vector3& centroid3d, + const Vector3* casterVertices, int casterVertexCount, const Vector3& centroid3d, float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) { - const int rays = SHADOW_RAY_COUNT; - // Validate the inputs. - if (vertexCount < 3 || heightFactor <= 0 || rays <= 0 - || geomFactor <= 0) { -#if DEBUG_SHADOW - ALOGW("Invalid input for createAmbientShadow(), early return!"); -#endif - return; - } + shadowVertexBuffer.setMode(VertexBuffer::kIndices); - Vector<Vector2> dir; // TODO: use C++11 unique_ptr - dir.setCapacity(rays); - float rayDist[rays]; - float rayHeight[rays]; - calculateRayDirections(rays, vertices, vertexCount, centroid3d, dir.editArray()); - - // Calculate the length and height of the points along the edge. - // - // The math here is: - // Intersect each ray (starting from the centroid) with the polygon. - for (int i = 0; i < rays; i++) { - int edgeIndex; - float edgeFraction; - float rayDistance; - calculateIntersection(vertices, vertexCount, centroid3d, dir[i], edgeIndex, - edgeFraction, rayDistance); - rayDist[i] = rayDistance; - if (edgeIndex < 0 || edgeIndex >= vertexCount) { -#if DEBUG_SHADOW - ALOGW("Invalid edgeIndex!"); -#endif - edgeIndex = 0; - } - float h1 = vertices[edgeIndex].z; - float h2 = vertices[((edgeIndex + 1) % vertexCount)].z; - rayHeight[i] = h1 + edgeFraction * (h2 - h1); - } + // In order to computer the outer vertices in one loop, we need pre-compute + // the normal by the vertex (n - 1) to vertex 0, and the spike and alpha value + // for vertex 0. + Vector2 previousNormal = getNormalFromVertices(casterVertices, + casterVertexCount - 1 , 0); + Vector2 currentSpike = {casterVertices[0].x - centroid3d.x, + casterVertices[0].y - centroid3d.y}; + currentSpike.normalize(); + float currentAlpha = getAlphaFromFactoredZ(casterVertices[0].z * heightFactor); - // The output buffer length basically is roughly rays * layers, but since we - // need triangle strips, so we need to duplicate vertices to accomplish that. + // Preparing all the output data. + int totalVertexCount, totalIndexCount, totalUmbraCount; + computeBufferSize(&totalVertexCount, &totalIndexCount, &totalUmbraCount, + casterVertexCount, isCasterOpaque); AlphaVertex* shadowVertices = - shadowVertexBuffer.alloc<AlphaVertex>(SHADOW_VERTEX_COUNT); - - // Calculate the vertex of the shadows. - // - // The math here is: - // Along the edges of the polygon, for each intersection point P (generated above), - // calculate the normal N, which should be perpendicular to the edge of the - // polygon (represented by the neighbor intersection points) . - // Shadow's vertices will be generated as : P + N * scale. - const Vector2 centroid2d = {centroid3d.x, centroid3d.y}; - for (int rayIndex = 0; rayIndex < rays; rayIndex++) { - Vector2 normal = {1.0f, 0.0f}; - calculateNormal(rays, rayIndex, dir.array(), rayDist, normal); - - // The vertex should be start from rayDist[i] then scale the - // normalizeNormal! - Vector2 intersection = dir[rayIndex] * rayDist[rayIndex] + - centroid2d; - - // outer ring of points, expanded based upon height of each ray intersection - float expansionDist = rayHeight[rayIndex] * heightFactor * - geomFactor; - AlphaVertex::set(&shadowVertices[rayIndex], - intersection.x + normal.x * expansionDist, - intersection.y + normal.y * expansionDist, - 0.0f); - - // inner ring of points - float opacity = 1.0 / (1 + rayHeight[rayIndex] * heightFactor); - // NOTE: Shadow alpha values are transformed when stored in alphavertices, - // so that they can be consumed directly by gFS_Main_ApplyVertexAlphaShadowInterp - float transformedOpacity = acos(1.0f - 2.0f * opacity); - AlphaVertex::set(&shadowVertices[rays + rayIndex], - intersection.x, - intersection.y, - transformedOpacity); - } + shadowVertexBuffer.alloc<AlphaVertex>(totalVertexCount); + int vertexBufferIndex = 0; + uint16_t* indexBuffer = shadowVertexBuffer.allocIndices<uint16_t>(totalIndexCount); + int indexBufferIndex = 0; + uint16_t umbraVertices[totalUmbraCount]; + int umbraIndex = 0; - if (isCasterOpaque) { - // skip inner ring, calc bounds over filled portion of buffer - shadowVertexBuffer.computeBounds<AlphaVertex>(2 * rays); - shadowVertexBuffer.setMode(VertexBuffer::kOnePolyRingShadow); - } else { - // If caster isn't opaque, we need to to fill the umbra by storing the umbra's - // centroid in the innermost ring of vertices. - float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor); - AlphaVertex centroidXYA; - AlphaVertex::set(¢roidXYA, centroid2d.x, centroid2d.y, centroidAlpha); - for (int rayIndex = 0; rayIndex < rays; rayIndex++) { - shadowVertices[2 * rays + rayIndex] = centroidXYA; - } - // calc bounds over entire buffer - shadowVertexBuffer.computeBounds<AlphaVertex>(); - shadowVertexBuffer.setMode(VertexBuffer::kTwoPolyRingShadow); - } + for (int i = 0; i < casterVertexCount; i++) { + // Corner: first figure out the extra vertices we need for the corner. + const Vector3& innerVertex = casterVertices[i]; + Vector2 currentNormal = getNormalFromVertices(casterVertices, i, + (i + 1) % casterVertexCount); + + int extraVerticesNumber = getExtraVertexNumber(currentNormal, previousNormal, + CORNER_RADIANS_DIVISOR); + float expansionDist = innerVertex.z * heightFactor * geomFactor; + const int cornerSlicesNumber = extraVerticesNumber + 1; // Minimal as 1. #if DEBUG_SHADOW - for (int i = 0; i < SHADOW_VERTEX_COUNT; i++) { - ALOGD("ambient shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x, - shadowVertices[i].y, shadowVertices[i].alpha); - } + ALOGD("cornerSlicesNumber is %d", cornerSlicesNumber); #endif -} -/** - * Generate an array of rays' direction vectors. - * To make sure the vertices generated are clockwise, the directions are from PI - * to -PI. - * - * @param rays The number of rays shooting out from the centroid. - * @param vertices Vertices of the polygon. - * @param vertexCount The number of vertices. - * @param centroid3d The centroid of the polygon. - * @param dir Return the array of ray vectors. - */ -void AmbientShadow::calculateRayDirections(const int rays, const Vector3* vertices, - const int vertexCount, const Vector3& centroid3d, Vector2* dir) { - // If we don't have enough rays, then fall back to the uniform distribution. - if (vertexCount * 2 > rays) { - float deltaAngle = 2 * M_PI / rays; - for (int i = 0; i < rays; i++) { - dir[i].x = cosf(M_PI - deltaAngle * i); - dir[i].y = sinf(M_PI - deltaAngle * i); + // Corner: fill the corner Vertex Buffer(VB) and Index Buffer(IB). + // We fill the inner vertex first, such that we can fill the index buffer + // inside the loop. + int currentInnerVertexIndex = vertexBufferIndex; + if (!isCasterOpaque) { + umbraVertices[umbraIndex++] = vertexBufferIndex; } - return; - } + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], casterVertices[i].x, + casterVertices[i].y, + getTransformedAlphaFromAlpha(currentAlpha)); - // If we have enough rays, then we assign each vertices a ray, and distribute - // the rest uniformly. - float rayThetas[rays]; - - const int uniformRayCount = rays - vertexCount; - const float deltaAngle = 2 * M_PI / uniformRayCount; - - // We have to generate all the vertices' theta anyway and we also need to - // find the minimal, so let's precompute it first. - // Since the incoming polygon is clockwise, we can find the dip to identify - // the minimal theta. - float polyThetas[vertexCount]; - int maxPolyThetaIndex = 0; - for (int i = 0; i < vertexCount; i++) { - polyThetas[i] = atan2(vertices[i].y - centroid3d.y, - vertices[i].x - centroid3d.x); - if (i > 0 && polyThetas[i] > polyThetas[i - 1]) { - maxPolyThetaIndex = i; - } - } + const Vector3& innerStart = casterVertices[i]; - // Both poly's thetas and uniform thetas are in decrease order(clockwise) - // from PI to -PI. - int polyThetaIndex = maxPolyThetaIndex; - float polyTheta = polyThetas[maxPolyThetaIndex]; - int uniformThetaIndex = 0; - float uniformTheta = M_PI; - for (int i = 0; i < rays; i++) { - // Compare both thetas and pick the smaller one and move on. - bool hasThetaCollision = abs(polyTheta - uniformTheta) < MINIMAL_DELTA_THETA; - if (polyTheta > uniformTheta || hasThetaCollision) { - if (hasThetaCollision) { - // Shift the uniformTheta to middle way between current polyTheta - // and next uniform theta. The next uniform theta can wrap around - // to exactly PI safely here. - // Note that neither polyTheta nor uniformTheta can be FLT_MAX - // due to the hasThetaCollision is true. - uniformTheta = (polyTheta + M_PI - deltaAngle * (uniformThetaIndex + 1)) / 2; -#if DEBUG_SHADOW - ALOGD("Shifted uniformTheta to %f", uniformTheta); -#endif - } - rayThetas[i] = polyTheta; - polyThetaIndex = (polyThetaIndex + 1) % vertexCount; - if (polyThetaIndex != maxPolyThetaIndex) { - polyTheta = polyThetas[polyThetaIndex]; - } else { - // out of poly points. - polyTheta = - FLT_MAX; - } - } else { - rayThetas[i] = uniformTheta; - uniformThetaIndex++; - if (uniformThetaIndex < uniformRayCount) { - uniformTheta = M_PI - deltaAngle * uniformThetaIndex; - } else { - // out of uniform points. - uniformTheta = - FLT_MAX; + // outerStart is the first outer vertex for this inner vertex. + // outerLast is the last outer vertex for this inner vertex. + Vector2 outerStart = {0, 0}; + Vector2 outerLast = {0, 0}; + // This will create vertices from [0, cornerSlicesNumber] inclusively, + // which means minimally 2 vertices even without the extra ones. + for (int j = 0; j <= cornerSlicesNumber; j++) { + Vector2 averageNormal = + previousNormal * (cornerSlicesNumber - j) + currentNormal * j; + averageNormal /= cornerSlicesNumber; + averageNormal.normalize(); + Vector2 outerVertex; + outerVertex.x = innerVertex.x + averageNormal.x * expansionDist; + outerVertex.y = innerVertex.y + averageNormal.y * expansionDist; + + indexBuffer[indexBufferIndex++] = vertexBufferIndex; + indexBuffer[indexBufferIndex++] = currentInnerVertexIndex; + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], outerVertex.x, + outerVertex.y, OUTER_OPACITY); + + if (j == 0) { + outerStart = outerVertex; + } else if (j == cornerSlicesNumber) { + outerLast = outerVertex; } } - } + previousNormal = currentNormal; + + // Edge: first figure out the extra vertices needed for the edge. + const Vector3& innerNext = casterVertices[(i + 1) % casterVertexCount]; + float nextAlpha = getAlphaFromFactoredZ(innerNext.z * heightFactor); + if (needsExtraForEdge(currentAlpha, nextAlpha)) { + // TODO: See if we can / should cache this outer vertex across the loop. + Vector2 outerNext; + float expansionDist = innerNext.z * heightFactor * geomFactor; + outerNext.x = innerNext.x + currentNormal.x * expansionDist; + outerNext.y = innerNext.y + currentNormal.y * expansionDist; - for (int i = 0; i < rays; i++) { + // Compute the angle and see how many extra points we need. + int extraVerticesNumber = getEdgeExtraAndUpdateSpike(¤tSpike, + innerNext, centroid3d); #if DEBUG_SHADOW - ALOGD("No. %d : %f", i, rayThetas[i] * 180 / M_PI); + ALOGD("extraVerticesNumber %d for edge %d", extraVerticesNumber, i); #endif - // TODO: Fix the intersection precision problem and remvoe the delta added - // here. - dir[i].x = cosf(rayThetas[i] + MINIMAL_DELTA_THETA); - dir[i].y = sinf(rayThetas[i] + MINIMAL_DELTA_THETA); - } -} + // Edge: fill the edge's VB and IB. + // This will create vertices pair from [1, extraVerticesNumber - 1]. + // If there is no extra vertices created here, the edge will be drawn + // as just 2 triangles. + for (int k = 1; k < extraVerticesNumber; k++) { + int startWeight = extraVerticesNumber - k; + Vector2 currentOuter = + (outerLast * startWeight + outerNext * k) / extraVerticesNumber; + indexBuffer[indexBufferIndex++] = vertexBufferIndex; + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentOuter.x, + currentOuter.y, OUTER_OPACITY); -/** - * Calculate the intersection of a ray hitting the polygon. - * - * @param vertices The shadow caster's polygon, which is represented in a - * Vector3 array. - * @param vertexCount The length of caster's polygon in terms of number of vertices. - * @param start The starting point of the ray. - * @param dir The direction vector of the ray. - * - * @param outEdgeIndex Return the index of the segment (or index of the starting - * vertex) that ray intersect with. - * @param outEdgeFraction Return the fraction offset from the segment starting - * index. - * @param outRayDist Return the ray distance from centroid to the intersection. - */ -void AmbientShadow::calculateIntersection(const Vector3* vertices, int vertexCount, - const Vector3& start, const Vector2& dir, int& outEdgeIndex, - float& outEdgeFraction, float& outRayDist) { - float startX = start.x; - float startY = start.y; - float dirX = dir.x; - float dirY = dir.y; - // Start the search from the last edge from poly[len-1] to poly[0]. - int p1 = vertexCount - 1; - - for (int p2 = 0; p2 < vertexCount; p2++) { - float p1x = vertices[p1].x; - float p1y = vertices[p1].y; - float p2x = vertices[p2].x; - float p2y = vertices[p2].y; - - // The math here is derived from: - // f(t, v) = p1x * (1 - t) + p2x * t - (startX + dirX * v) = 0; - // g(t, v) = p1y * (1 - t) + p2y * t - (startY + dirY * v) = 0; - float div = (dirX * (p1y - p2y) + dirY * p2x - dirY * p1x); - if (div != 0) { - float t = (dirX * (p1y - startY) + dirY * startX - dirY * p1x) / (div); - if (t > 0 && t <= 1) { - float t2 = (p1x * (startY - p2y) - + p2x * (p1y - startY) - + startX * (p2y - p1y)) / div; - if (t2 > 0) { - outEdgeIndex = p1; - outRayDist = t2; - outEdgeFraction = t; - return; + if (!isCasterOpaque) { + umbraVertices[umbraIndex++] = vertexBufferIndex; } + Vector3 currentInner = + (innerStart * startWeight + innerNext * k) / extraVerticesNumber; + indexBuffer[indexBufferIndex++] = vertexBufferIndex; + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentInner.x, + currentInner.y, + getTransformedAlphaFromFactoredZ(currentInner.z * heightFactor)); } } - p1 = p2; + currentAlpha = nextAlpha; } - return; -}; -/** - * Calculate the normal at the intersection point between a ray and the polygon. - * - * @param rays The total number of rays. - * @param currentRayIndex The index of the ray which the normal is based on. - * @param dir The array of the all the rays directions. - * @param rayDist The pre-computed ray distances array. - * - * @param normal Return the normal. - */ -void AmbientShadow::calculateNormal(int rays, int currentRayIndex, - const Vector2* dir, const float* rayDist, Vector2& normal) { - int preIndex = (currentRayIndex - 1 + rays) % rays; - int postIndex = (currentRayIndex + 1) % rays; - Vector2 p1 = dir[preIndex] * rayDist[preIndex]; - Vector2 p2 = dir[postIndex] * rayDist[postIndex]; - - // Now the rays are going CW around the poly. - Vector2 delta = p2 - p1; - if (delta.length() != 0) { - delta.normalize(); - // Calculate the normal , which is CCW 90 rotate to the delta. - normal.x = - delta.y; - normal.y = delta.x; + indexBuffer[indexBufferIndex++] = 1; + indexBuffer[indexBufferIndex++] = 0; + + if (!isCasterOpaque) { + // Add the centroid as the last one in the vertex buffer. + float centroidOpacity = + getTransformedAlphaFromFactoredZ(centroid3d.z * heightFactor); + int centroidIndex = vertexBufferIndex; + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid3d.x, + centroid3d.y, centroidOpacity); + + for (int i = 0; i < umbraIndex; i++) { + // Note that umbraVertices[0] is always 0. + // So the start and the end of the umbra are using the "0". + // And penumbra ended with 0, so a degenerated triangle is formed b/t + // the umbra and penumbra. + indexBuffer[indexBufferIndex++] = umbraVertices[i]; + indexBuffer[indexBufferIndex++] = centroidIndex; + } + indexBuffer[indexBufferIndex++] = 0; } + + // At the end, update the real index and vertex buffer size. + shadowVertexBuffer.updateVertexCount(vertexBufferIndex); + shadowVertexBuffer.updateIndexCount(indexBufferIndex); + + checkOverflow(vertexBufferIndex, totalVertexCount, "Vertex Buffer"); + checkOverflow(indexBufferIndex, totalIndexCount, "Index Buffer"); + checkOverflow(umbraIndex, totalUmbraCount, "Umbra Buffer"); + +#if DEBUG_SHADOW + for (int i = 0; i < vertexBufferIndex; i++) { + ALOGD("vertexBuffer i %d, (%f, %f %f)", i, shadowVertices[i].x, shadowVertices[i].y, + shadowVertices[i].alpha); + } + for (int i = 0; i < indexBufferIndex; i++) { + ALOGD("indexBuffer i %d, indexBuffer[i] %d", i, indexBuffer[i]); + } +#endif } }; // namespace uirenderer diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h index 68df246..9660dc0 100644 --- a/libs/hwui/AmbientShadow.h +++ b/libs/hwui/AmbientShadow.h @@ -28,27 +28,12 @@ namespace uirenderer { /** * AmbientShadow is used to calculate the ambient shadow value around a polygon. - * - * TODO: calculateIntersection() now is O(N*M), where N is the number of - * polygon's vertics and M is the number of rays. In fact, by staring tracing - * the vertex from the previous intersection, the algorithm can be O(N + M); */ class AmbientShadow { public: static void createAmbientShadow(bool isCasterOpaque, const Vector3* poly, int polyLength, const Vector3& centroid3d, float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer); - -private: - static void calculateRayDirections(const int rays, const Vector3* vertices, - const int vertexCount, const Vector3& centroid3d, Vector2* dir); - - static void calculateIntersection(const Vector3* poly, int nbVertices, - const Vector3& start, const Vector2& dir, int& outEdgeIndex, - float& outEdgeFraction, float& outRayDist); - - static void calculateNormal(int rays, int currentRayIndex, const Vector2* dir, - const float* rayDist, Vector2& normal); }; // AmbientShadow }; // namespace uirenderer diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index 1c697d5..da65f38 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -95,6 +95,8 @@ void BaseRenderNodeAnimator::pushStaging(AnimationContext& context) { // Oh boy, we're starting! Man the battle stations! if (mPlayState == RUNNING) { transitionToRunning(context); + } else if (mPlayState == FINISHED) { + callOnFinishedListener(context); } } } diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index bbf0551..0f36c06 100755 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -2417,6 +2417,10 @@ status_t OpenGLRenderer::drawVertexBuffer(float translateX, float translateY, } else if (mode == VertexBuffer::kTwoPolyRingShadow) { mCaches.bindShadowIndicesBuffer(); glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0); + } else if (mode == VertexBuffer::kIndices) { + mCaches.unbindIndicesBuffer(); + glDrawElements(GL_TRIANGLE_STRIP, vertexBuffer.getIndexCount(), GL_UNSIGNED_SHORT, + vertexBuffer.getIndices()); } if (isAA) { diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h index 2a9f01c..d033ed9 100644 --- a/libs/hwui/Vector.h +++ b/libs/hwui/Vector.h @@ -111,6 +111,23 @@ public: float y; float z; + Vector3 operator+(const Vector3& v) const { + return (Vector3){x + v.x, y + v.y, z + v.z}; + } + + Vector3 operator-(const Vector3& v) const { + return (Vector3){x - v.x, y - v.y, z - v.z}; + } + + Vector3 operator/(float s) const { + return (Vector3){x / s, y / s, z / s}; + } + + Vector3 operator*(float s) const { + return (Vector3){x * s, y * s, z * s}; + } + + void dump() { ALOGD("Vector3[%.2f, %.2f, %.2f]", x, y, z); } diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h index 3837f88..966fa4e 100644 --- a/libs/hwui/VertexBuffer.h +++ b/libs/hwui/VertexBuffer.h @@ -17,6 +17,7 @@ #ifndef ANDROID_HWUI_VERTEX_BUFFER_H #define ANDROID_HWUI_VERTEX_BUFFER_H +#include "utils/MathUtils.h" namespace android { namespace uirenderer { @@ -26,19 +27,27 @@ public: enum Mode { kStandard = 0, kOnePolyRingShadow = 1, - kTwoPolyRingShadow = 2 + kTwoPolyRingShadow = 2, + kIndices = 3 }; VertexBuffer() : mBuffer(0) + , mIndices(0) , mVertexCount(0) + , mIndexCount(0) + , mAllocatedVertexCount(0) + , mAllocatedIndexCount(0) , mByteCount(0) , mMode(kStandard) + , mReallocBuffer(0) , mCleanupMethod(NULL) + , mCleanupIndexMethod(NULL) {} ~VertexBuffer() { if (mCleanupMethod) mCleanupMethod(mBuffer); + if (mCleanupIndexMethod) mCleanupIndexMethod(mIndices); } /** @@ -59,6 +68,7 @@ public: mReallocBuffer = reallocBuffer + vertexCount; return reallocBuffer; } + mAllocatedVertexCount = vertexCount; mVertexCount = vertexCount; mByteCount = mVertexCount * sizeof(TYPE); mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount]; @@ -69,6 +79,17 @@ public: } template <class TYPE> + TYPE* allocIndices(int indexCount) { + mAllocatedIndexCount = indexCount; + mIndexCount = indexCount; + mIndices = (void*)new TYPE[indexCount]; + + mCleanupIndexMethod = &(cleanup<TYPE>); + + return (TYPE*)mIndices; + } + + template <class TYPE> void copyInto(const VertexBuffer& srcBuffer, float xOffset, float yOffset) { int verticesToCopy = srcBuffer.getVertexCount(); @@ -103,9 +124,17 @@ public: } const void* getBuffer() const { return mBuffer; } + const void* getIndices() const { return mIndices; } const Rect& getBounds() const { return mBounds; } unsigned int getVertexCount() const { return mVertexCount; } unsigned int getSize() const { return mByteCount; } + unsigned int getIndexCount() const { return mIndexCount; } + void updateIndexCount(unsigned int newCount) { + mIndexCount = MathUtils::min(newCount, mAllocatedIndexCount); + } + void updateVertexCount(unsigned int newCount) { + newCount = MathUtils::min(newCount, mAllocatedVertexCount); + } Mode getMode() const { return mMode; } void setBounds(Rect bounds) { mBounds = bounds; } @@ -127,14 +156,22 @@ private: } Rect mBounds; + void* mBuffer; + void* mIndices; + unsigned int mVertexCount; + unsigned int mIndexCount; + unsigned int mAllocatedVertexCount; + unsigned int mAllocatedIndexCount; unsigned int mByteCount; + Mode mMode; void* mReallocBuffer; // used for multi-allocation void (*mCleanupMethod)(void*); + void (*mCleanupIndexMethod)(void*); }; }; // namespace uirenderer diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index ecfedf6..491a295 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -314,6 +314,7 @@ void CanvasContext::destroyHardwareResources() { stopDrawing(); if (mEglManager.hasEglContext()) { requireGlContext(); + freePrefetechedLayers(); mRootRenderNode->destroyHardwareResources(); Caches::getInstance().flush(Caches::kFlushMode_Layers); } diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 032f07f..3e8ee93 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -557,12 +557,15 @@ final public class MediaCodec { int i = 0; for (Map.Entry<String, Object> entry: formatMap.entrySet()) { if (entry.getKey().equals(MediaFormat.KEY_AUDIO_SESSION_ID)) { - // TODO: Wire up as soon as AudioService is ready. Check entry.getValue() for - // non-integral type. - // long audioHwSync = audioService.getAudioHwSyncForSession(entry.getValue()); - long audioHwSync = 0; + int sessionId = 0; + try { + sessionId = (Integer)entry.getValue(); + } + catch (Exception e) { + throw new IllegalArgumentException("Wrong Session ID Parameter!"); + } keys[i] = "audio-hw-sync"; - values[i] = audioHwSync; + values[i] = AudioSystem.getAudioHwSyncForSession(sessionId); } else { keys[i] = entry.getKey(); values[i] = entry.getValue(); diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 711831b..ae8ce4b 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -683,6 +683,7 @@ public final class MediaSession { * * @param mediaButtonIntent an intent containing the KeyEvent as an * extra + * @return True if the event was handled, false otherwise. */ public boolean onMediaButtonEvent(@NonNull Intent mediaButtonIntent) { if (mSession != null @@ -695,36 +696,43 @@ public final class MediaSession { case KeyEvent.KEYCODE_MEDIA_PLAY: if ((validActions & PlaybackState.ACTION_PLAY) != 0) { onPlay(); + return true; } break; case KeyEvent.KEYCODE_MEDIA_PAUSE: if ((validActions & PlaybackState.ACTION_PAUSE) != 0) { onPause(); + return true; } break; case KeyEvent.KEYCODE_MEDIA_NEXT: if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) { onSkipToNext(); + return true; } break; case KeyEvent.KEYCODE_MEDIA_PREVIOUS: if ((validActions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0) { onSkipToPrevious(); + return true; } break; case KeyEvent.KEYCODE_MEDIA_STOP: if ((validActions & PlaybackState.ACTION_STOP) != 0) { onStop(); + return true; } break; case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: if ((validActions & PlaybackState.ACTION_FAST_FORWARD) != 0) { onFastForward(); + return true; } break; case KeyEvent.KEYCODE_MEDIA_REWIND: if ((validActions & PlaybackState.ACTION_REWIND) != 0) { onRewind(); + return true; } break; case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: @@ -737,8 +745,10 @@ public final class MediaSession { | PlaybackState.ACTION_PAUSE)) != 0; if (isPlaying && canPause) { onPause(); + return true; } else if (!isPlaying && canPlay) { onPlay(); + return true; } break; } diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java index 0bda2b3..93b40fb 100644 --- a/media/java/android/media/tv/TvContentRating.java +++ b/media/java/android/media/tv/TvContentRating.java @@ -126,7 +126,7 @@ import java.util.Objects; * code snippet: * </p> * <pre> - * String rating = TvContentRating.createRating( + * TvContentRating rating = TvContentRating.createRating( * "com.android.tv", * "US_TV", * "US_TV_PG", @@ -136,7 +136,7 @@ import java.util.Objects; * <table> * <tr> * <th>Constant Value</th> - * <th>Comment</th> + * <th>Description</th> * </tr> * <tr> * <td>com.android.tv</td> @@ -148,7 +148,7 @@ import java.util.Objects; * <table> * <tr> * <th>Constant Value</th> - * <th>Comment</th> + * <th>Description</th> * </tr> * <tr> * <td>AM_TV_RS</td> @@ -346,7 +346,7 @@ import java.util.Objects; * <tr> * <th>Rating System</th> * <th>Constant Value</th> - * <th>Comment</th> + * <th>Description</th> * </tr> * <tr> * <td valign="top" rowspan="6">AM_TV_RS</td> @@ -1419,7 +1419,7 @@ import java.util.Objects; * <tr> * <th>Rating System</th> * <th>Constant Value</th> - * <th>Comment</th> + * <th>Description</th> * </tr> * <tr> * <td valign="top" rowspan="6">NL_TV</td> diff --git a/packages/DocumentsUI/res/color/item_root_icon.xml b/packages/DocumentsUI/res/color/item_root_icon.xml new file mode 100644 index 0000000..1374e61 --- /dev/null +++ b/packages/DocumentsUI/res/color/item_root_icon.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_focused="true" android:state_activated="true" android:color="@*android:color/primary_text_default_material_dark" /> + <item android:state_focused="false" android:state_activated="true" android:color="@*android:color/primary_text_default_material_dark" /> + <item android:color="@*android:color/secondary_text_material_light" /> +</selector> diff --git a/packages/DocumentsUI/res/color/item_root_primary_text.xml b/packages/DocumentsUI/res/color/item_root_primary_text.xml new file mode 100644 index 0000000..7e703fa --- /dev/null +++ b/packages/DocumentsUI/res/color/item_root_primary_text.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_focused="true" android:state_activated="true" android:color="@*android:color/primary_text_default_material_dark" /> + <item android:state_focused="false" android:state_activated="true" android:color="@*android:color/primary_text_default_material_dark" /> + <item android:state_enabled="false" android:alpha="@*android:dimen/disabled_alpha_material_light" android:color="@*android:color/primary_text_default_material_light" /> + <item android:color="@*android:color/primary_text_default_material_light" /> +</selector> diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_grid_selection_check.png b/packages/DocumentsUI/res/drawable-hdpi/ic_grid_selection_check.png Binary files differnew file mode 100644 index 0000000..f3007c2 --- /dev/null +++ b/packages/DocumentsUI/res/drawable-hdpi/ic_grid_selection_check.png diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_grid_selection_check.png b/packages/DocumentsUI/res/drawable-mdpi/ic_grid_selection_check.png Binary files differnew file mode 100644 index 0000000..16f2ab9 --- /dev/null +++ b/packages/DocumentsUI/res/drawable-mdpi/ic_grid_selection_check.png diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_grid_selection_check.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_grid_selection_check.png Binary files differnew file mode 100644 index 0000000..0885320 --- /dev/null +++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_grid_selection_check.png diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_grid_selection_check.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_grid_selection_check.png Binary files differnew file mode 100644 index 0000000..083bbcc --- /dev/null +++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_grid_selection_check.png diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_grid_selection_check.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_grid_selection_check.png Binary files differnew file mode 100644 index 0000000..74b1ca5 --- /dev/null +++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_grid_selection_check.png diff --git a/packages/DocumentsUI/res/drawable/item_doc_grid_overlay.xml b/packages/DocumentsUI/res/drawable/item_doc_grid_overlay.xml new file mode 100644 index 0000000..3fbd25e --- /dev/null +++ b/packages/DocumentsUI/res/drawable/item_doc_grid_overlay.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_focused="true" android:state_activated="true" android:drawable="@color/item_doc_grid_overlay_activated" /> + <item android:state_focused="false" android:state_activated="true" android:drawable="@color/item_doc_grid_overlay_activated" /> + <item android:state_enabled="false" android:drawable="@color/item_doc_grid_overlay_disabled" /> + <item android:drawable="@android:color/transparent" /> +</selector> diff --git a/packages/DocumentsUI/res/drawable/item_doc_grid_overlay_icon.xml b/packages/DocumentsUI/res/drawable/item_doc_grid_overlay_icon.xml new file mode 100644 index 0000000..d40de1e --- /dev/null +++ b/packages/DocumentsUI/res/drawable/item_doc_grid_overlay_icon.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="true"> + <item android:state_focused="true" android:state_activated="true" android:drawable="@drawable/ic_grid_selection_check" /> + <item android:state_focused="false" android:state_activated="true" android:drawable="@drawable/ic_grid_selection_check" /> + <item android:drawable="@android:color/transparent" /> +</selector> diff --git a/packages/DocumentsUI/res/drawable/item_activated_overlay.xml b/packages/DocumentsUI/res/drawable/item_doc_list_background.xml index 83e4d7e..b879542 100644 --- a/packages/DocumentsUI/res/drawable/item_activated_overlay.xml +++ b/packages/DocumentsUI/res/drawable/item_doc_list_background.xml @@ -15,11 +15,7 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_focused="true" android:state_activated="true"> - <color android:color="@color/accent_color_overlay" /> - </item> - <item android:state_focused="false" android:state_activated="true"> - <color android:color="@color/accent_color_overlay" /> - </item> + <item android:state_focused="true" android:state_activated="true" android:drawable="@color/item_doc_list_background_activated" /> + <item android:state_focused="false" android:state_activated="true" android:drawable="@color/item_doc_list_background_activated" /> <item android:drawable="@android:color/transparent" /> </selector> diff --git a/packages/DocumentsUI/res/drawable/item_activated.xml b/packages/DocumentsUI/res/drawable/item_root_background.xml index 1b3f44a..1b3f44a 100644 --- a/packages/DocumentsUI/res/drawable/item_activated.xml +++ b/packages/DocumentsUI/res/drawable/item_root_background.xml diff --git a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml index 95af7e9..d124320 100644 --- a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml +++ b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml @@ -17,7 +17,7 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/item_activated"> + android:background="@drawable/item_doc_list_background"> <LinearLayout android:layout_width="match_parent" diff --git a/packages/DocumentsUI/res/layout/item_doc_grid.xml b/packages/DocumentsUI/res/layout/item_doc_grid.xml index bdb3184..d62d050 100644 --- a/packages/DocumentsUI/res/layout/item_doc_grid.xml +++ b/packages/DocumentsUI/res/layout/item_doc_grid.xml @@ -17,9 +17,7 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="@dimen/grid_item_height" - android:orientation="vertical" - android:background="@color/grid_item_background" - android:foreground="@drawable/item_activated_overlay"> + android:background="@color/item_doc_grid_background"> <ImageView android:id="@+id/icon_thumb" @@ -44,7 +42,7 @@ <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/grid_protect_background" + android:background="@color/item_doc_grid_protect_background" android:orientation="vertical" android:paddingStart="16dp" android:paddingEnd="12dp" @@ -68,7 +66,7 @@ android:ellipsize="middle" android:textAlignment="viewStart" android:textAppearance="@android:style/TextAppearance.Material.Subhead" - android:textColor="?android:attr/textColorPrimaryInverse" /> + android:textColor="@*android:color/primary_text_default_material_dark" /> <ImageView android:id="@android:id/icon1" @@ -97,7 +95,7 @@ android:ellipsize="end" android:textAlignment="viewStart" android:textAppearance="@android:style/TextAppearance.Material.Caption" - android:textColor="?android:attr/textColorPrimaryInverse" /> + android:textColor="@*android:color/primary_text_default_material_dark" /> <TextView android:id="@+id/size" @@ -109,7 +107,7 @@ android:ellipsize="end" android:textAlignment="viewStart" android:textAppearance="@android:style/TextAppearance.Material.Caption" - android:textColor="?android:attr/textColorPrimaryInverse" /> + android:textColor="@*android:color/primary_text_default_material_dark" /> <ImageView android:id="@android:id/icon2" @@ -126,4 +124,19 @@ </LinearLayout> + <ImageView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:src="@drawable/item_doc_grid_overlay" + android:contentDescription="@null" + android:duplicateParentState="true" /> + + <ImageView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:src="@drawable/item_doc_grid_overlay_icon" + android:scaleType="center" + android:contentDescription="@null" + android:duplicateParentState="true" /> + </FrameLayout> diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml index c5f1842..c576669 100644 --- a/packages/DocumentsUI/res/layout/item_doc_list.xml +++ b/packages/DocumentsUI/res/layout/item_doc_list.xml @@ -17,7 +17,7 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/item_activated"> + android:background="@drawable/item_doc_list_background"> <LinearLayout android:layout_width="match_parent" diff --git a/packages/DocumentsUI/res/layout/item_root.xml b/packages/DocumentsUI/res/layout/item_root.xml index 266b9b0..bd83923 100644 --- a/packages/DocumentsUI/res/layout/item_root.xml +++ b/packages/DocumentsUI/res/layout/item_root.xml @@ -23,19 +23,21 @@ android:gravity="center_vertical" android:orientation="horizontal" android:baselineAligned="false" - android:background="@drawable/item_activated"> + android:background="@drawable/item_root_background"> <FrameLayout android:layout_width="@dimen/icon_size" android:layout_height="@dimen/icon_size" - android:layout_marginEnd="16dp"> + android:layout_marginEnd="16dp" + android:duplicateParentState="true"> <ImageView android:id="@android:id/icon" android:layout_width="@dimen/root_icon_size" android:layout_height="match_parent" android:scaleType="centerInside" - android:contentDescription="@null" /> + android:contentDescription="@null" + android:duplicateParentState="true" /> </FrameLayout> @@ -54,7 +56,7 @@ android:ellipsize="end" android:textAlignment="viewStart" android:textAppearance="@android:style/TextAppearance.Material.Body1" - android:textColor="?android:attr/textColorPrimary" /> + android:textColor="@color/item_root_primary_text" /> <TextView android:id="@android:id/summary" @@ -64,7 +66,7 @@ android:ellipsize="end" android:textAlignment="viewStart" android:textAppearance="@android:style/TextAppearance.Material.Body1" - android:textColor="?android:attr/textColorSecondary" /> + android:textColor="@color/item_root_primary_text" /> </LinearLayout> diff --git a/packages/DocumentsUI/res/values/colors.xml b/packages/DocumentsUI/res/values/colors.xml index 7442b09..2ceb968 100644 --- a/packages/DocumentsUI/res/values/colors.xml +++ b/packages/DocumentsUI/res/values/colors.xml @@ -18,8 +18,16 @@ <color name="material_grey_50">#fffafafa</color> <color name="material_grey_300">#ffeeeeee</color> - <!-- Half-alpha of material_teal_500 --> - <color name="accent_color_overlay">#8800bcd4</color> + <color name="item_doc_grid_background">@color/material_grey_300</color> + + <color name="item_doc_grid_protect_background">#88000000</color> + + <color name="item_doc_grid_overlay_activated">#88000000</color> + <color name="item_doc_grid_overlay_disabled">#88ffffff</color> + + <color name="item_doc_list_overlay_disabled">#88ffffff</color> + + <!-- 10% alpha of material_deep_teal_500 --> + <color name="item_doc_list_background_activated">#1a009688</color> - <color name="grid_item_background">@color/material_grey_300</color> </resources> diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml index 5cfe046..7693da3 100644 --- a/packages/DocumentsUI/res/values/styles.xml +++ b/packages/DocumentsUI/res/values/styles.xml @@ -28,6 +28,7 @@ <item name="android:colorAccent">@*android:color/material_deep_teal_500</item> <item name="android:windowActionBar">false</item> + <item name="android:windowActionModeOverlay">true</item> <item name="android:windowNoTitle">true</item> <item name="*android:windowFixedWidthMajor">@null</item> diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java index caa07ab..39c2252 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java @@ -807,6 +807,9 @@ public class DirectoryFragment extends Fragment { || MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, docMimeType); final boolean showThumbnail = supportsThumbnail && allowThumbnail && !mSvelteRecents; + final boolean enabled = isDocumentEnabled(docMimeType, docFlags); + final float iconAlpha = (state.derivedMode == MODE_LIST && !enabled) ? 0.5f : 1f; + boolean cacheHit = false; if (showThumbnail) { final Uri uri = DocumentsContract.buildDocumentUri(docAuthority, docId); @@ -817,7 +820,7 @@ public class DirectoryFragment extends Fragment { } else { iconThumb.setImageDrawable(null); final ThumbnailAsyncTask task = new ThumbnailAsyncTask( - uri, iconMime, iconThumb, mThumbSize); + uri, iconMime, iconThumb, mThumbSize, iconAlpha); iconThumb.setTag(task); ProviderExecutor.forAuthority(docAuthority).execute(task); } @@ -886,7 +889,7 @@ public class DirectoryFragment extends Fragment { // hint to remind user they're a directory. if (Document.MIME_TYPE_DIR.equals(docMimeType) && state.derivedMode == MODE_GRID && showThumbnail) { - iconDrawable = IconUtils.applyTint(context, R.drawable.ic_doc_folder, + iconDrawable = IconUtils.applyTintAttr(context, R.drawable.ic_doc_folder, android.R.attr.textColorPrimaryInverse); } @@ -940,20 +943,12 @@ public class DirectoryFragment extends Fragment { line2.setVisibility(hasLine2 ? View.VISIBLE : View.GONE); } - final boolean enabled = isDocumentEnabled(docMimeType, docFlags); - if (enabled) { - setEnabledRecursive(convertView, true); - iconMime.setAlpha(1f); - iconThumb.setAlpha(1f); - if (icon1 != null) icon1.setAlpha(1f); - if (icon2 != null) icon2.setAlpha(1f); - } else { - setEnabledRecursive(convertView, false); - iconMime.setAlpha(0.5f); - iconThumb.setAlpha(0.5f); - if (icon1 != null) icon1.setAlpha(0.5f); - if (icon2 != null) icon2.setAlpha(0.5f); - } + setEnabledRecursive(convertView, enabled); + + iconMime.setAlpha(iconAlpha); + iconThumb.setAlpha(iconAlpha); + if (icon1 != null) icon1.setAlpha(iconAlpha); + if (icon2 != null) icon2.setAlpha(iconAlpha); return convertView; } @@ -1000,14 +995,16 @@ public class DirectoryFragment extends Fragment { private final ImageView mIconMime; private final ImageView mIconThumb; private final Point mThumbSize; + private final float mTargetAlpha; private final CancellationSignal mSignal; - public ThumbnailAsyncTask( - Uri uri, ImageView iconMime, ImageView iconThumb, Point thumbSize) { + public ThumbnailAsyncTask(Uri uri, ImageView iconMime, ImageView iconThumb, Point thumbSize, + float targetAlpha) { mUri = uri; mIconMime = iconMime; mIconThumb = iconThumb; mThumbSize = thumbSize; + mTargetAlpha = targetAlpha; mSignal = new CancellationSignal(); } @@ -1051,11 +1048,10 @@ public class DirectoryFragment extends Fragment { mIconThumb.setTag(null); mIconThumb.setImageBitmap(result); - final float targetAlpha = mIconMime.isEnabled() ? 1f : 0.5f; - mIconMime.setAlpha(targetAlpha); + mIconMime.setAlpha(mTargetAlpha); mIconMime.animate().alpha(0f).start(); mIconThumb.setAlpha(0f); - mIconThumb.animate().alpha(targetAlpha).start(); + mIconThumb.animate().alpha(mTargetAlpha).start(); } } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java index b2e38fc..416aeb0 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java +++ b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java @@ -266,15 +266,16 @@ public class IconUtils { } } - public static Drawable applyTint(Context context, int drawableId, int tintAttrId) { - final Resources res = context.getResources(); - - final TypedValue outValue = new TypedValue(); - context.getTheme().resolveAttribute(tintAttrId, outValue, true); - + public static Drawable applyTintColor(Context context, int drawableId, int tintColorId) { final Drawable icon = context.getDrawable(drawableId); icon.mutate(); - icon.setTintList(res.getColorStateList(outValue.resourceId)); + icon.setTintList(context.getResources().getColorStateList(tintColorId)); return icon; } + + public static Drawable applyTintAttr(Context context, int drawableId, int tintAttrId) { + final TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(tintAttrId, outValue, true); + return applyTintColor(context, drawableId, outValue.resourceId); + } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java index a465ecd..f81690a 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java @@ -58,7 +58,7 @@ import java.util.concurrent.TimeUnit; * Cache of known storage backends and their roots. */ public class RootsCache { - private static final boolean LOGD = true; + private static final boolean LOGD = false; public static final Uri sNotificationUri = Uri.parse( "content://com.android.documentsui.roots/"); diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java index b19e028..884cf31 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java @@ -235,7 +235,7 @@ public class RootsFragment extends Fragment { final TextView summary = (TextView) convertView.findViewById(android.R.id.summary); final Context context = convertView.getContext(); - icon.setImageDrawable(root.loadIcon(context)); + icon.setImageDrawable(root.loadDrawerIcon(context)); title.setText(root.title); // Show available space if no summary diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java index fcfe518..97d8ed0 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java +++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java @@ -211,9 +211,17 @@ public class RootInfo implements Durable, Parcelable { } } + public Drawable loadDrawerIcon(Context context) { + if (derivedIcon != 0) { + return IconUtils.applyTintColor(context, derivedIcon, R.color.item_root_icon); + } else { + return IconUtils.loadPackageIcon(context, authority, icon); + } + } + public Drawable loadGridIcon(Context context) { if (derivedIcon != 0) { - return IconUtils.applyTint(context, derivedIcon, + return IconUtils.applyTintAttr(context, derivedIcon, android.R.attr.textColorPrimaryInverse); } else { return IconUtils.loadPackageIcon(context, authority, icon); @@ -222,7 +230,7 @@ public class RootInfo implements Durable, Parcelable { public Drawable loadToolbarIcon(Context context) { if (derivedIcon != 0) { - return IconUtils.applyTint(context, derivedIcon, + return IconUtils.applyTintAttr(context, derivedIcon, android.R.attr.colorControlNormal); } else { return IconUtils.loadPackageIcon(context, authority, icon); diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 299e50c..a3bed4f 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -187,6 +187,9 @@ <!-- Default for Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1==on --> <integer name="def_lock_screen_show_notifications">1</integer> + <!-- Default for Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS --> + <bool name="def_lock_screen_allow_private_notifications">true</bool> + <!-- Default for Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, 1==on --> <integer name="def_heads_up_enabled">1</integer> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index edefb13..fd5e6fe 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -70,7 +70,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion' // is properly propagated through your change. Not doing so will result in a loss of user // settings. - private static final int DATABASE_VERSION = 108; + private static final int DATABASE_VERSION = 109; private Context mContext; private int mUserHandle; @@ -1673,8 +1673,8 @@ public class DatabaseHelper extends SQLiteOpenHelper { try { stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)" + " VALUES(?,?);"); - loadBooleanSetting(stmt, Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, - R.bool.def_guest_user_enabled); + loadIntegerSetting(stmt, Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, + R.integer.def_lock_screen_show_notifications); if (mUserHandle == UserHandle.USER_OWNER) { final int oldShow = getIntValueFromTable(db, TABLE_GLOBAL, Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, -1); @@ -1733,6 +1733,22 @@ public class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 108; } + if (upgradeVersion < 109) { + db.beginTransaction(); + SQLiteStatement stmt = null; + try { + stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)" + + " VALUES(?,?);"); + loadBooleanSetting(stmt, Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, + R.bool.def_lock_screen_allow_private_notifications); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + if (stmt != null) stmt.close(); + } + upgradeVersion = 109; + } + // *** Remember to update DATABASE_VERSION above! if (upgradeVersion != currentVersion) { @@ -2301,6 +2317,9 @@ public class DatabaseHelper extends SQLiteOpenHelper { loadIntegerSetting(stmt, Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, R.integer.def_lock_screen_show_notifications); + loadBooleanSetting(stmt, Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, + R.bool.def_lock_screen_allow_private_notifications); + } finally { if (stmt != null) stmt.close(); } diff --git a/packages/SystemUI/res/drawable/ic_android.xml b/packages/SystemUI/res/drawable/ic_android.xml new file mode 100644 index 0000000..19ee9a7 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_android.xml @@ -0,0 +1,24 @@ +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:pathData="M6.000000,18.000000c0.000000,0.600000 0.400000,1.000000 1.000000,1.000000l1.000000,0.000000l0.000000,3.500000C8.000000,23.299999 8.700000,24.000000 9.500000,24.000000c0.800000,0.000000 1.500000,-0.700000 1.500000,-1.500000L11.000000,19.000000l2.000000,0.000000l0.000000,3.500000c0.000000,0.800000 0.700000,1.500000 1.500000,1.500000c0.800000,0.000000 1.500000,-0.700000 1.500000,-1.500000L16.000000,19.000000l1.000000,0.000000c0.600000,0.000000 1.000000,-0.400000 1.000000,-1.000000L18.000000,8.000000L6.000000,8.000000L6.000000,18.000000zM3.500000,8.000000C2.700000,8.000000 2.000000,8.700000 2.000000,9.500000l0.000000,7.000000C2.000000,17.299999 2.700000,18.000000 3.500000,18.000000C4.300000,18.000000 5.000000,17.299999 5.000000,16.500000l0.000000,-7.000000C5.000000,8.700000 4.300000,8.000000 3.500000,8.000000zM20.500000,8.000000C19.700001,8.000000 19.000000,8.700000 19.000000,9.500000l0.000000,7.000000c0.000000,0.800000 0.700000,1.500000 1.500000,1.500000c0.800000,0.000000 1.500000,-0.700000 1.500000,-1.500000l0.000000,-7.000000C22.000000,8.700000 21.299999,8.000000 20.500000,8.000000zM15.500000,2.200000l1.300000,-1.300000c0.200000,-0.200000 0.200000,-0.500000 0.000000,-0.700000c-0.200000,-0.200000 -0.500000,-0.200000 -0.700000,0.000000l-1.500000,1.500000C13.900000,1.200000 13.000000,1.000000 12.000000,1.000000c-1.000000,0.000000 -1.900000,0.200000 -2.700000,0.600000L7.900000,0.100000C7.700000,0.000000 7.300000,0.000000 7.100000,0.100000C7.000000,0.300000 7.000000,0.700000 7.100000,0.900000l1.300000,1.300000C7.000000,3.300000 6.000000,5.000000 6.000000,7.000000l12.000000,0.000000C18.000000,5.000000 17.000000,3.200000 15.500000,2.200000zM10.000000,5.000000L9.000000,5.000000L9.000000,4.000000l1.000000,0.000000L10.000000,5.000000zM15.000000,5.000000l-1.000000,0.000000L14.000000,4.000000l1.000000,0.000000L15.000000,5.000000z" + android:fillColor="#ffffffff"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_close.xml b/packages/SystemUI/res/drawable/ic_close.xml new file mode 100644 index 0000000..7d93d45 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_close.xml @@ -0,0 +1,24 @@ +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z" + android:fillColor="#FF000000"/> +</vector> diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index ef0c9bb..ca07c87 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -67,6 +67,6 @@ android:src="@drawable/ic_lock_24dp" android:scaleType="center" android:tint="#ffffffff" - android:contentDescription="@string/accessibility_unlock_button_not_secured" /> + android:contentDescription="@string/accessibility_unlock_button" /> </com.android.systemui.statusbar.phone.KeyguardBottomAreaView> diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml index eff3758..351177b 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml @@ -38,8 +38,8 @@ android:id="@+id/more_text" android:layout_width="32dp" android:layout_height="32dp" - android:layout_marginStart="20dp" - android:layout_marginEnd="16dp" + android:layout_marginStart="16dp" + android:layout_marginEnd="12dp" android:layout_gravity="center_vertical" android:background="@drawable/keyguard_overflow_number_background" android:gravity="center" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index b000a48..b488c56 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -213,6 +213,8 @@ <string name="accessibility_camera_button">Camera</string> <!-- Content description of the phone button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_phone_button">Phone</string> + <!-- Content description of the unlock button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_unlock_button">Unlock</string> <!-- Click action label for accessibility for the unlock button. [CHAR LIMIT=NONE] --> <string name="unlock_label">unlock</string> <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] --> @@ -220,17 +222,6 @@ <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] --> <string name="camera_label">open camera</string> - <!-- Content description of the lock icon when device is secured (lock closed) and trust not managed (not shown on the screen). [CHAR LIMIT=NONE] --> - <string name="accessibility_unlock_button_secured">Device secured.</string> - <!-- Content description of the lock icon when device is not secured (lock open) and trust not managed (not shown on the screen). [CHAR LIMIT=NONE] --> - <string name="accessibility_unlock_button_not_secured">Device not secured.</string> - <!-- Content description of the lock icon when device is secured (lock closed) and trust managed (not shown on the screen). [CHAR LIMIT=NONE] --> - <string name="accessibility_unlock_button_secured_trust_managed">Device secured, trust agent active.</string> - <!-- Content description of the lock icon when device is not secured (lock open) and trust managed (not shown on the screen). [CHAR LIMIT=NONE] --> - <string name="accessibility_unlock_button_not_secured_trust_managed">Device not secured, trust agent active.</string> - <!-- Content description of the lock icon when face unlock is running (face icon) and trust managed (not shown on the screen). [CHAR LIMIT=NONE] --> - <string name="accessibility_unlock_button_face_unlock_running">Face detection running, trust agent active.</string> - <!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_ime_switch_button">Switch input method button.</string> <!-- Content description of the compatibility zoom button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> @@ -323,6 +314,15 @@ <!-- Content description of an item with full signal for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_signal_full">Signal full.</string> + <!-- Content description of an item that is turned on for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_desc_on">On.</string> + <!-- Content description of an item that is turned off for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_desc_off">Off.</string> + <!-- Content description of an item that is connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_desc_connected">Connected.</string> + <!-- Content description of an item that is connecting for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_desc_connecting">Connecting.</string> + <!-- Content description of the data connection type GPRS for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_data_connection_gprs">GPRS</string> @@ -894,6 +894,18 @@ <!-- Indication on the keyguard that appears when the user disables trust agents until the next time they unlock manually. [CHAR LIMIT=NONE] --> <string name="keyguard_indication_trust_disabled">Device will stay locked until you manually unlock</string> + <!-- Title of notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=40] --> + <string name="hidden_notifications_title">Get notifications faster</string> + + <!-- Body of notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=60] --> + <string name="hidden_notifications_text">See them before you unlock</string> + + <!-- Cancel action for notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=10] --> + <string name="hidden_notifications_cancel">No thanks</string> + + <!-- continue action for notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=10] --> + <string name="hidden_notifications_setup">Set up</string> + <!-- Indication that the current volume and other effects (vibration) are being suppressed by a third party, such as a notification listener. [CHAR LIMIT=30] --> <string name="muted_by">Muted by <xliff:g id="third_party">%1$s</xliff:g></string> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 1ac3bc3..0c6e7b6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -67,6 +67,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV ArrayList<TaskStack> mStacks; View mSearchBar; RecentsViewCallbacks mCb; + boolean mAlreadyLaunchingTask; public RecentsView(Context context) { super(context); @@ -120,6 +121,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } addView(stackView); } + + // Reset the launched state + mAlreadyLaunchingTask = false; } /** Removes all the task stack views from this recents view. */ @@ -381,6 +385,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV if (mCb != null) { mCb.onTaskViewClicked(); } + // Skip if we are already launching tasks + if (mAlreadyLaunchingTask) { + return; + } + mAlreadyLaunchingTask = true; // Upfront the processing of the thumbnail TaskViewTransform transform = new TaskViewTransform(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index c8adf61..f04c8c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -22,6 +22,7 @@ import android.animation.TimeInterpolator; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.Notification; +import android.app.NotificationManager; import android.app.PendingIntent; import android.app.TaskStackBuilder; import android.app.admin.DevicePolicyManager; @@ -35,7 +36,11 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.content.res.Configuration; +import android.content.res.Resources; import android.database.ContentObserver; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Build; @@ -78,6 +83,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; import com.android.internal.util.NotificationColorUtil; +import com.android.internal.widget.LockPatternUtils; import com.android.systemui.R; import com.android.systemui.RecentsComponent; import com.android.systemui.SearchPanelView; @@ -126,6 +132,12 @@ public abstract class BaseStatusBar extends SystemUI implements public static final int EXPANDED_LEAVE_ALONE = -10000; public static final int EXPANDED_FULL_OPEN = -10001; + private static final int HIDDEN_NOTIFICATION_ID = 10000; + private static final String BANNER_ACTION_CANCEL = + "com.android.systemui.statusbar.banner_action_cancel"; + private static final String BANNER_ACTION_SETUP = + "com.android.systemui.statusbar.banner_action_setup"; + protected CommandQueue mCommandQueue; protected IStatusBarService mBarService; protected H mHandler = createHandler(); @@ -308,6 +320,20 @@ public abstract class BaseStatusBar extends SystemUI implements mUsersAllowingPrivateNotifications.clear(); updateLockscreenNotificationSetting(); updateNotifications(); + } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) { + NotificationManager noMan = (NotificationManager) + mContext.getSystemService(Context.NOTIFICATION_SERVICE); + noMan.cancel(HIDDEN_NOTIFICATION_ID); + + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0); + if (BANNER_ACTION_SETUP.equals(action)) { + animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */); + mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + + ); + } } } }; @@ -490,12 +516,61 @@ public abstract class BaseStatusBar extends SystemUI implements IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(Intent.ACTION_USER_ADDED); + filter.addAction(BANNER_ACTION_CANCEL); + filter.addAction(BANNER_ACTION_SETUP); filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); mContext.registerReceiver(mBroadcastReceiver, filter); updateCurrentProfilesCache(); } + protected void notifyUserAboutHiddenNotifications() { + if (0 != Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) { + Log.d(TAG, "user hasn't seen notification about hidden notifications"); + final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext); + if (!lockPatternUtils.isSecure()) { + Log.d(TAG, "insecure lockscreen, skipping notification"); + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0); + return; + } + Log.d(TAG, "disabling lockecreen notifications and alerting the user"); + // disable lockscreen notifications until user acts on the banner. + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0); + + final String packageName = mContext.getPackageName(); + PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0, + new Intent(BANNER_ACTION_CANCEL).setPackage(packageName), + PendingIntent.FLAG_CANCEL_CURRENT); + PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0, + new Intent(BANNER_ACTION_SETUP).setPackage(packageName), + PendingIntent.FLAG_CANCEL_CURRENT); + + final Resources res = mContext.getResources(); + final int colorRes = com.android.internal.R.color.system_notification_accent_color; + Notification.Builder note = new Notification.Builder(mContext) + .setSmallIcon(R.drawable.ic_android) + .setContentTitle(mContext.getString(R.string.hidden_notifications_title)) + .setContentText(mContext.getString(R.string.hidden_notifications_text)) + .setPriority(Notification.PRIORITY_HIGH) + .setOngoing(true) + .setColor(res.getColor(colorRes)) + .setContentIntent(setupIntent) + .addAction(R.drawable.ic_close, + mContext.getString(R.string.hidden_notifications_cancel), + cancelIntent) + .addAction(R.drawable.ic_settings, + mContext.getString(R.string.hidden_notifications_setup), + setupIntent); + + NotificationManager noMan = + (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + noMan.notify(HIDDEN_NOTIFICATION_ID, note.build()); + } + } + public void userSwitched(int newUserId) { // should be overridden } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 62552b2..f9da30f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -380,21 +380,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mLockIcon.setImageResource(iconRes); boolean trustManaged = mUnlockMethodCache.isTrustManaged(); mTrustDrawable.setTrustManaged(trustManaged); - updateLockIconClickability(); - updateLockIconContentDescription(mUnlockMethodCache.isFaceUnlockRunning(), - mUnlockMethodCache.isMethodInsecure(), trustManaged); } - private void updateLockIconContentDescription(boolean faceUnlockRunning, boolean insecure, - boolean trustManaged) { - mLockIcon.setContentDescription(getResources().getString( - faceUnlockRunning ? R.string.accessibility_unlock_button_face_unlock_running - : insecure && !trustManaged ? R.string.accessibility_unlock_button_not_secured - : insecure ? R.string.accessibility_unlock_button_not_secured_trust_managed - : !trustManaged ? R.string.accessibility_unlock_button_secured - : R.string.accessibility_unlock_button_secured_trust_managed)); - } + public KeyguardAffordanceView getPhoneView() { return mPhoneImageView; 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 9fd3d9c..1a0d2d4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -582,6 +582,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, putComponent(PhoneStatusBar.class, this); setControllerUsers(); + + notifyUserAboutHiddenNotifications(); } // ================================================================================ @@ -3612,7 +3614,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } public boolean onSpacePressed() { - if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { + if (mScreenOn != null && mScreenOn + && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) { animateCollapsePanels(0 /* flags */, true /* force */); return true; } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java index f03c5eb..12c887e 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java @@ -26,7 +26,6 @@ import android.content.DialogInterface.OnDismissListener; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.content.res.Resources; @@ -46,7 +45,6 @@ import android.net.Uri; import android.os.Handler; import android.os.Message; import android.os.Vibrator; -import android.provider.Settings.Global; import android.util.Log; import android.util.SparseArray; import android.view.KeyEvent; @@ -58,6 +56,7 @@ import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; +import android.view.accessibility.AccessibilityManager; import android.widget.ImageView; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; @@ -97,6 +96,7 @@ public class VolumePanel extends Handler { private static final int TIMEOUT_DELAY_SHORT = 1500; private static final int TIMEOUT_DELAY_COLLAPSED = 4500; private static final int TIMEOUT_DELAY_SAFETY_WARNING = 5000; + private static final int TIMEOUT_DELAY_SAFETY_WARNING_TALKBACK = 25000; private static final int TIMEOUT_DELAY_EXPANDED = 10000; private static final int MSG_VOLUME_CHANGED = 0; @@ -161,6 +161,7 @@ public class VolumePanel extends Handler { private int mActiveStreamType = -1; /** All the slider controls mapped by stream type */ private SparseArray<StreamControl> mStreamControls; + private final AccessibilityManager mAccessibilityManager; private enum StreamResources { BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO, @@ -332,6 +333,8 @@ public class VolumePanel extends Handler { mContext = context; mZenController = zenController; mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + mAccessibilityManager = (AccessibilityManager) context.getSystemService( + Context.ACCESSIBILITY_SERVICE); // For now, only show master volume if master volume is supported final Resources res = context.getResources(); @@ -791,7 +794,8 @@ public class VolumePanel extends Handler { } private void updateTimeoutDelay() { - mTimeoutDelay = sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING + mTimeoutDelay = sSafetyWarning != null ? mAccessibilityManager.isEnabled() ? + TIMEOUT_DELAY_SAFETY_WARNING_TALKBACK : TIMEOUT_DELAY_SAFETY_WARNING : mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED @@ -1214,6 +1218,7 @@ public class VolumePanel extends Handler { } updateStates(); } + updateTimeoutDelay(); resetTimeout(); } diff --git a/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java b/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java index 71b0d53..6f79f58 100644 --- a/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java +++ b/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java @@ -83,6 +83,7 @@ public class EnableAccessibilityController { private final Context mContext; + private final Runnable mOnAccessibilityEnabledCallback; private final UserManager mUserManager; private final TextToSpeech mTts; private final Ringtone mTone; @@ -97,8 +98,9 @@ public class EnableAccessibilityController { private float mSecondPointerDownX; private float mSecondPointerDownY; - public EnableAccessibilityController(Context context) { + public EnableAccessibilityController(Context context, Runnable onAccessibilityEnabledCallback) { mContext = context; + mOnAccessibilityEnabledCallback = onAccessibilityEnabledCallback; mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mTts = new TextToSpeech(context, new TextToSpeech.OnInitListener() { @Override @@ -275,5 +277,7 @@ public class EnableAccessibilityController { /* ignore */ } } + + mOnAccessibilityEnabledCallback.run(); } } diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java index ae94654..41695c1 100644 --- a/policy/src/com/android/internal/policy/impl/GlobalActions.java +++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java @@ -1073,7 +1073,13 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac // is dismissed on the first down while the global gesture is a long press // with two fingers anywhere on the screen. if (EnableAccessibilityController.canEnableAccessibilityViaGesture(mContext)) { - mEnableAccessibilityController = new EnableAccessibilityController(mContext); + mEnableAccessibilityController = new EnableAccessibilityController(mContext, + new Runnable() { + @Override + public void run() { + dismiss(); + } + }); super.setCanceledOnTouchOutside(false); } else { mEnableAccessibilityController = null; diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index a13da609..93591a9 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -2754,11 +2754,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mStatusColorView = updateColorViewInt(mStatusColorView, SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS, mStatusBarColor, mLastTopInset, Gravity.TOP, - STATUS_BAR_BACKGROUND_TRANSITION_NAME); + STATUS_BAR_BACKGROUND_TRANSITION_NAME, + com.android.internal.R.id.statusBarBackground); mNavigationColorView = updateColorViewInt(mNavigationColorView, SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION, mNavigationBarColor, mLastBottomInset, Gravity.BOTTOM, - NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME); + NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME, + com.android.internal.R.id.navigationBarBackground); } if (insets != null) { insets = insets.consumeStableInsets(); @@ -2767,7 +2769,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } private View updateColorViewInt(View view, int systemUiHideFlag, int translucentFlag, - int color, int height, int verticalGravity, String transitionName) { + int color, int height, int verticalGravity, String transitionName, int id) { boolean show = height > 0 && (mLastSystemUiVisibility & systemUiHideFlag) == 0 && (getAttributes().flags & translucentFlag) == 0 && (color & Color.BLACK) != 0 @@ -2778,6 +2780,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { view = new View(mContext); view.setBackgroundColor(color); view.setTransitionName(transitionName); + view.setId(id); addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, height, Gravity.START | verticalGravity)); } diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java index ac0ca0a..af5c13d 100644 --- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java @@ -708,6 +708,7 @@ class TouchExplorer implements EventStreamTransformation { // Send an event to the end of the drag gesture. sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); } + mCurrentState = STATE_TOUCH_EXPLORING; } break; case MotionEvent.ACTION_UP: { mAms.onTouchInteractionEnd(); diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java index 86ce961..92f5170 100644 --- a/services/core/java/com/android/server/LockSettingsService.java +++ b/services/core/java/com/android/server/LockSettingsService.java @@ -16,9 +16,12 @@ package com.android.server; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.UserInfo; @@ -31,6 +34,7 @@ import android.database.sqlite.SQLiteStatement; import android.os.Binder; import android.os.Environment; import android.os.IBinder; +import android.os.Process; import android.os.RemoteException; import android.os.storage.IMountService; import android.os.ServiceManager; @@ -41,11 +45,14 @@ import android.os.UserManager; import android.provider.Settings; import android.provider.Settings.Secure; import android.provider.Settings.SettingNotFoundException; +import android.security.KeyChain; +import android.security.KeyChain.KeyChainConnection; import android.security.KeyStore; import android.text.TextUtils; import android.util.Log; import android.util.Slog; +import com.android.internal.os.BackgroundThread; import com.android.internal.widget.ILockSettings; import com.android.internal.widget.ILockSettingsObserver; import com.android.internal.widget.LockPatternUtils; @@ -99,8 +106,30 @@ public class LockSettingsService extends ILockSettings.Stub { mLockPatternUtils = new LockPatternUtils(context); mFirstCallToVold = true; + + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_USER_ADDED); + mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); } + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // Update keystore settings for profiles which use the same password as their parent + if (Intent.ACTION_USER_STARTED.equals(intent.getAction())) { + final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); + final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); + final UserInfo parentInfo = um.getProfileParent(userHandle); + if (parentInfo != null) { + final KeyStore ks = KeyStore.getInstance(); + final int profileUid = UserHandle.getUid(userHandle, Process.SYSTEM_UID); + final int parentUid = UserHandle.getUid(parentInfo.id, Process.SYSTEM_UID); + ks.syncUid(parentUid, profileUid); + } + } + } + }; + public void systemReady() { migrateOldData(); } @@ -275,6 +304,17 @@ public class LockSettingsService extends ILockSettings.Stub { } } + private int getUserParentOrSelfId(int userId) { + if (userId != 0) { + final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); + final UserInfo pi = um.getProfileParent(userId); + if (pi != null) { + return pi.id; + } + } + return userId; + } + private String getLockPatternFilename(int userId) { String dataSystemDirectory = android.os.Environment.getDataDirectory().getAbsolutePath() + @@ -283,6 +323,7 @@ public class LockSettingsService extends ILockSettings.Stub { // Leave it in the same place for user 0 return dataSystemDirectory + LOCK_PATTERN_FILE; } else { + userId = getUserParentOrSelfId(userId); return new File(Environment.getUserSystemDirectory(userId), LOCK_PATTERN_FILE) .getAbsolutePath(); } @@ -296,7 +337,8 @@ public class LockSettingsService extends ILockSettings.Stub { // Leave it in the same place for user 0 return dataSystemDirectory + LOCK_PASSWORD_FILE; } else { - return new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE) + userId = getUserParentOrSelfId(userId); + return new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE) .getAbsolutePath(); } } @@ -315,16 +357,27 @@ public class LockSettingsService extends ILockSettings.Stub { return new File(getLockPatternFilename(userId)).length() > 0; } - private void maybeUpdateKeystore(String password, int userId) { - if (userId == UserHandle.USER_OWNER) { - final KeyStore keyStore = KeyStore.getInstance(); - // Conditionally reset the keystore if empty. If non-empty, we are just - // switching key guard type - if (TextUtils.isEmpty(password) && keyStore.isEmpty()) { - keyStore.reset(); + private void maybeUpdateKeystore(String password, int userHandle) { + final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); + final KeyStore ks = KeyStore.getInstance(); + + final List<UserInfo> profiles = um.getProfiles(userHandle); + boolean shouldReset = TextUtils.isEmpty(password); + + // For historical reasons, don't wipe a non-empty keystore if we have a single user with a + // single profile. + if (userHandle == UserHandle.USER_OWNER && profiles.size() == 1) { + if (!ks.isEmpty()) { + shouldReset = false; + } + } + + for (UserInfo pi : profiles) { + final int profileUid = UserHandle.getUid(pi.id, Process.SYSTEM_UID); + if (shouldReset) { + ks.resetUid(profileUid); } else { - // Update the keystore password - keyStore.password(password); + ks.passwordUid(password, profileUid); } } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 6310764..c2c86ff 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -9087,9 +9087,11 @@ public final class ActivityManagerService extends ActivityManagerNative boolean singleton; if (!providerRunning) { try { + checkTime(startTime, "getContentProviderImpl: before resolveContentProvider"); cpi = AppGlobals.getPackageManager(). resolveContentProvider(name, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId); + checkTime(startTime, "getContentProviderImpl: after resolveContentProvider"); } catch (RemoteException ex) { } if (cpi == null) { @@ -9106,12 +9108,15 @@ public final class ActivityManagerService extends ActivityManagerNative userId = UserHandle.USER_OWNER; } cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId); + checkTime(startTime, "getContentProviderImpl: got app info for user"); String msg; + checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission"); if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton)) != null) { throw new SecurityException(msg); } + checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission"); if (!mProcessesReady && !mDidUpdate && !mWaitingUpdate && !cpi.processName.equals("system")) { @@ -9133,15 +9138,19 @@ public final class ActivityManagerService extends ActivityManagerNative } ComponentName comp = new ComponentName(cpi.packageName, cpi.name); + checkTime(startTime, "getContentProviderImpl: before getProviderByClass"); cpr = mProviderMap.getProviderByClass(comp, userId); + checkTime(startTime, "getContentProviderImpl: after getProviderByClass"); final boolean firstClass = cpr == null; if (firstClass) { try { + checkTime(startTime, "getContentProviderImpl: before getApplicationInfo"); ApplicationInfo ai = AppGlobals.getPackageManager(). getApplicationInfo( cpi.applicationInfo.packageName, STOCK_PM_FLAGS, userId); + checkTime(startTime, "getContentProviderImpl: after getApplicationInfo"); if (ai == null) { Slog.w(TAG, "No package info for content provider " + cpi.name); @@ -9154,6 +9163,8 @@ public final class ActivityManagerService extends ActivityManagerNative } } + checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord"); + if (r != null && cpr.canRunHere(r)) { // If this is a multiprocess provider, then just return its // info and allow the caller to instantiate it. Only do @@ -9187,8 +9198,10 @@ public final class ActivityManagerService extends ActivityManagerNative try { // Content provider is now in use, its package can't be stopped. try { + checkTime(startTime, "getContentProviderImpl: before set stopped state"); AppGlobals.getPackageManager().setPackageStoppedState( cpr.appInfo.packageName, false, userId); + checkTime(startTime, "getContentProviderImpl: after set stopped state"); } catch (RemoteException e) { } catch (IllegalArgumentException e) { Slog.w(TAG, "Failed trying to unstop package " @@ -9196,22 +9209,26 @@ public final class ActivityManagerService extends ActivityManagerNative } // Use existing process if already started + checkTime(startTime, "getContentProviderImpl: looking for process record"); ProcessRecord proc = getProcessRecordLocked( cpi.processName, cpr.appInfo.uid, false); if (proc != null && proc.thread != null) { if (DEBUG_PROVIDER) { Slog.d(TAG, "Installing in existing process " + proc); } + checkTime(startTime, "getContentProviderImpl: scheduling install"); proc.pubProviders.put(cpi.name, cpr); try { proc.thread.scheduleInstallProvider(cpi); } catch (RemoteException e) { } } else { + checkTime(startTime, "getContentProviderImpl: before start process"); proc = startProcessLocked(cpi.processName, cpr.appInfo, false, 0, "content provider", new ComponentName(cpi.applicationInfo.packageName, cpi.name), false, false, false); + checkTime(startTime, "getContentProviderImpl: after start process"); if (proc == null) { Slog.w(TAG, "Unable to launch app " + cpi.applicationInfo.packageName + "/" @@ -9227,6 +9244,8 @@ public final class ActivityManagerService extends ActivityManagerNative } } + checkTime(startTime, "getContentProviderImpl: updating data structures"); + // Make sure the provider is published (the same provider class // may be published under multiple names). if (firstClass) { @@ -9239,6 +9258,7 @@ public final class ActivityManagerService extends ActivityManagerNative conn.waiting = true; } } + checkTime(startTime, "getContentProviderImpl: done!"); } // Wait for the provider to be published... diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 1d2f7a9..6545134 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -2072,7 +2072,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } targetStack = inTask.stack; targetStack.moveTaskToFrontLocked(inTask, r, options); - mWindowManager.moveTaskToTop(targetStack.topTask().taskId); + mWindowManager.moveTaskToTop(inTask.taskId); // Check whether we should actually launch the new activity in to the task, // or just reuse the current activity on top. diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java index ff22764..a37249d 100644 --- a/services/core/java/com/android/server/am/ContentProviderRecord.java +++ b/services/core/java/com/android/server/am/ContentProviderRecord.java @@ -166,8 +166,16 @@ final class ContentProviderRecord { } if (full) { if (hasExternalProcessHandles()) { - pw.print(prefix); pw.print("externals="); - pw.println(externalProcessTokenToHandle.size()); + pw.print(prefix); pw.print("externals:"); + if (externalProcessTokenToHandle != null) { + pw.print(" w/token="); + pw.print(externalProcessTokenToHandle.size()); + } + if (externalProcessNoHandleCount > 0) { + pw.print(" notoken="); + pw.print(externalProcessNoHandleCount); + } + pw.println(); } } else { if (connections.size() > 0 || externalProcessNoHandleCount > 0) { diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 38077eb..8c342dd 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1009,7 +1009,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call y[i] = normalizeAbsoluteBrightness(brightness[i]); } - Spline spline = Spline.createMonotoneCubicSpline(x, y); + Spline spline = Spline.createSpline(x, y); if (DEBUG) { Slog.d(TAG, "Auto-brightness spline: " + spline); for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) { diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 379ec94..c3bc306 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -819,6 +819,7 @@ public class JobSchedulerService extends com.android.server.SystemService }; void dumpInternal(PrintWriter pw) { + final long now = SystemClock.elapsedRealtime(); synchronized (mJobs) { pw.print("Started users: "); for (int i=0; i<mStartedUsers.size(); i++) { @@ -833,15 +834,14 @@ public class JobSchedulerService extends com.android.server.SystemService job.dump(pw, " "); } } else { - pw.println(); - pw.println("No jobs scheduled."); + pw.println(" None."); } for (int i=0; i<mControllers.size(); i++) { pw.println(); mControllers.get(i).dumpControllerState(pw); } pw.println(); - pw.println("Pending"); + pw.println("Pending:"); for (int i=0; i<mPendingJobs.size(); i++) { pw.println(mPendingJobs.get(i).hashCode()); } @@ -852,10 +852,14 @@ public class JobSchedulerService extends com.android.server.SystemService if (jsc.isAvailable()) { continue; } else { - pw.println(jsc.getRunningJob().hashCode() + " for: " + - (SystemClock.elapsedRealtime() - - jsc.getExecutionStartTimeElapsed())/1000 + "s " + - "timeout: " + jsc.getTimeoutElapsed()); + final long timeout = jsc.getTimeoutElapsed(); + pw.print("Running for: "); + pw.print((now - jsc.getExecutionStartTimeElapsed())/1000); + pw.print("s timeout="); + pw.print(timeout); + pw.print(" fromnow="); + pw.println(timeout-now); + jsc.getRunningJob().dump(pw, " "); } } pw.println(); diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index 6f5d3c2..f562721 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -251,6 +251,7 @@ public class JobStatus { // Dumpsys infrastructure public void dump(PrintWriter pw, String prefix) { + pw.print(prefix); pw.println(this.toString()); } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index fc1b746..d0f4054 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -17,6 +17,8 @@ package com.android.server.notification; import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS; +import static android.service.notification.NotificationListenerService.TRIM_FULL; +import static android.service.notification.NotificationListenerService.TRIM_LIGHT; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_TAG; import static org.xmlpull.v1.XmlPullParser.START_TAG; @@ -50,6 +52,7 @@ import android.media.AudioManager; import android.media.IRingtonePlayer; import android.net.Uri; import android.os.Binder; +import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; @@ -1290,24 +1293,23 @@ public class NotificationManagerService extends SystemService { */ @Override public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener( - INotificationListener token, String[] keys) { + INotificationListener token, String[] keys, int trim) { synchronized (mNotificationList) { final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); - final ArrayList<StatusBarNotification> list - = new ArrayList<StatusBarNotification>(); final boolean getKeys = keys != null; final int N = getKeys ? keys.length : mNotificationList.size(); - list.ensureCapacity(N); + final ArrayList<StatusBarNotification> list + = new ArrayList<StatusBarNotification>(N); for (int i=0; i<N; i++) { final NotificationRecord r = getKeys ? mNotificationsByKey.get(keys[i]) : mNotificationList.get(i); - if (r != null) { - StatusBarNotification sbn = r.sbn; - if (isVisibleToListener(sbn, info)) { - list.add(sbn); - } - } + if (r == null) continue; + StatusBarNotification sbn = r.sbn; + if (!isVisibleToListener(sbn, info)) continue; + StatusBarNotification sbnToSend = + (trim == TRIM_FULL) ? sbn : sbn.cloneLight(); + list.add(sbnToSend); } return new ParceledListSlice<StatusBarNotification>(list); } @@ -1364,6 +1366,16 @@ public class NotificationManagerService extends SystemService { } @Override + public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim) + throws RemoteException { + synchronized (mNotificationList) { + final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); + if (info == null) return; + mListeners.setOnNotificationPostedTrimLocked(info, trim); + } + } + + @Override public ZenModeConfig getZenModeConfig() { enforceSystemOrSystemUI("INotificationManager.getZenModeConfig"); return mZenModeHelper.getConfig(); @@ -1427,7 +1439,7 @@ public class NotificationManagerService extends SystemService { protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump NotificationManager from from pid=" + pw.println("Permission Denial: can't dump NotificationManager from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); return; @@ -1441,6 +1453,13 @@ public class NotificationManagerService extends SystemService { enforceSystemOrSystemUI("INotificationManager.getEffectsSuppressor"); return mEffectsSuppressor; } + + @Override + public boolean matchesCallFilter(Bundle extras) { + enforceSystemOrSystemUI("INotificationManager.matchesCallFilter"); + return mZenModeHelper.matchesCallFilter(extras, + mRankingHelper.findExtractor(ValidateNotificationPeople.class)); + } }; private String[] getActiveNotificationKeys(INotificationListener token) { @@ -2611,6 +2630,8 @@ public class NotificationManagerService extends SystemService { public class NotificationListeners extends ManagedServices { + private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>(); + public NotificationListeners() { super(getContext(), mHandler, mNotificationList, mUserProfiles); } @@ -2651,6 +2672,20 @@ public class NotificationManagerService extends SystemService { if (mListenersDisablingEffects.remove(removed)) { updateListenerHintsLocked(); } + mLightTrimListeners.remove(removed); + } + + public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) { + if (trim == TRIM_LIGHT) { + mLightTrimListeners.add(info); + } else { + mLightTrimListeners.remove(info); + } + } + + public int getOnNotificationPostedTrim(ManagedServiceInfo info) { + return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL; + } /** @@ -2661,8 +2696,10 @@ public class NotificationManagerService extends SystemService { * but isn't anymore. */ public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) { - // make a copy in case changes are made to the underlying Notification object - final StatusBarNotification sbnClone = sbn.clone(); + // Lazily initialized snapshots of the notification. + StatusBarNotification sbnClone = null; + StatusBarNotification sbnCloneLight = null; + for (final ManagedServiceInfo info : mServices) { boolean sbnVisible = isVisibleToListener(sbn, info); boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false; @@ -2684,10 +2721,20 @@ public class NotificationManagerService extends SystemService { continue; } + final int trim = mListeners.getOnNotificationPostedTrim(info); + + if (trim == TRIM_LIGHT && sbnCloneLight == null) { + sbnCloneLight = sbn.cloneLight(); + } else if (trim == TRIM_FULL && sbnClone == null) { + sbnClone = sbn.clone(); + } + final StatusBarNotification sbnToPost = + (trim == TRIM_FULL) ? sbnClone : sbnCloneLight; + mHandler.post(new Runnable() { @Override public void run() { - notifyPosted(info, sbnClone, update); + notifyPosted(info, sbnToPost, update); } }); } diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index 01188af..435177b 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -87,6 +87,17 @@ public class RankingHelper implements RankingConfig { mProxyByGroupTmp = new ArrayMap<String, NotificationRecord>(); } + public <T extends NotificationSignalExtractor> T findExtractor(Class<T> extractorClass) { + final int N = mSignalExtractors.length; + for (int i = 0; i < N; i++) { + final NotificationSignalExtractor extractor = mSignalExtractors[i]; + if (extractorClass.equals(extractor.getClass())) { + return (T) extractor; + } + } + return null; + } + public void extractSignals(NotificationRecord r) { final int N = mSignalExtractors.length; for (int i = 0; i < N; i++) { diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java index bdc364c..aa47858 100644 --- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java +++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java @@ -71,8 +71,17 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { private LruCache<String, LookupResult> mPeopleCache; private RankingReconsideration validatePeople(final NotificationRecord record) { + final String key = record.getKey(); + final Bundle extras = record.getNotification().extras; + final float[] affinityOut = new float[1]; + final RankingReconsideration rr = validatePeople(key, extras, affinityOut); + record.setContactAffinity(affinityOut[0]); + return rr; + } + + private PeopleRankingReconsideration validatePeople(String key, Bundle extras, + float[] affinityOut) { float affinity = NONE; - Bundle extras = record.getNotification().extras; if (extras == null) { return null; } @@ -82,7 +91,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { return null; } - if (INFO) Slog.i(TAG, "Validating: " + record.sbn.getKey()); + if (INFO) Slog.i(TAG, "Validating: " + key); final LinkedList<String> pendingLookups = new LinkedList<String>(); for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) { final String handle = people[personIdx]; @@ -102,51 +111,15 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { } // record the best available data, so far: - record.setContactAffinity(affinity); + affinityOut[0] = affinity; if (pendingLookups.isEmpty()) { if (INFO) Slog.i(TAG, "final affinity: " + affinity); return null; } - if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + record.sbn.getKey()); - return new RankingReconsideration(record.getKey()) { - float mContactAffinity = NONE; - @Override - public void work() { - if (INFO) Slog.i(TAG, "Executing: validation for: " + record.getKey()); - for (final String handle: pendingLookups) { - LookupResult lookupResult = null; - final Uri uri = Uri.parse(handle); - if ("tel".equals(uri.getScheme())) { - if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle); - lookupResult = resolvePhoneContact(uri.getSchemeSpecificPart()); - } else if ("mailto".equals(uri.getScheme())) { - if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle); - lookupResult = resolveEmailContact(uri.getSchemeSpecificPart()); - } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) { - if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle); - lookupResult = searchContacts(uri); - } else { - lookupResult = new LookupResult(); // invalid person for the cache - Slog.w(TAG, "unsupported URI " + handle); - } - if (lookupResult != null) { - synchronized (mPeopleCache) { - mPeopleCache.put(handle, lookupResult); - } - mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity()); - } - } - } - - @Override - public void applyChangesLocked(NotificationRecord operand) { - float affinityBound = operand.getContactAffinity(); - operand.setContactAffinity(Math.max(mContactAffinity, affinityBound)); - if (INFO) Slog.i(TAG, "final affinity: " + operand.getContactAffinity()); - } - }; + if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + key); + return new PeopleRankingReconsideration(key, pendingLookups); } // VisibleForTesting @@ -269,6 +242,19 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { // ignore: config has no relevant information yet. } + public float getContactAffinity(Bundle extras) { + if (extras == null) return NONE; + final String key = Long.toString(System.nanoTime()); + final float[] affinityOut = new float[1]; + final PeopleRankingReconsideration prr = validatePeople(key, extras, affinityOut); + float affinity = affinityOut[0]; + if (prr != null) { + prr.work(); + affinity = Math.max(prr.getContactAffinity(), affinity); + } + return affinity; + } + private static class LookupResult { private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr public static final int INVALID_ID = -1; @@ -328,5 +314,55 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { return this; } } + + private class PeopleRankingReconsideration extends RankingReconsideration { + private final LinkedList<String> mPendingLookups; + + private float mContactAffinity = NONE; + + private PeopleRankingReconsideration(String key, LinkedList<String> pendingLookups) { + super(key); + mPendingLookups = pendingLookups; + } + + @Override + public void work() { + if (INFO) Slog.i(TAG, "Executing: validation for: " + mKey); + for (final String handle: mPendingLookups) { + LookupResult lookupResult = null; + final Uri uri = Uri.parse(handle); + if ("tel".equals(uri.getScheme())) { + if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle); + lookupResult = resolvePhoneContact(uri.getSchemeSpecificPart()); + } else if ("mailto".equals(uri.getScheme())) { + if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle); + lookupResult = resolveEmailContact(uri.getSchemeSpecificPart()); + } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) { + if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle); + lookupResult = searchContacts(uri); + } else { + lookupResult = new LookupResult(); // invalid person for the cache + Slog.w(TAG, "unsupported URI " + handle); + } + if (lookupResult != null) { + synchronized (mPeopleCache) { + mPeopleCache.put(handle, lookupResult); + } + mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity()); + } + } + } + + @Override + public void applyChangesLocked(NotificationRecord operand) { + float affinityBound = operand.getContactAffinity(); + operand.setContactAffinity(Math.max(mContactAffinity, affinityBound)); + if (INFO) Slog.i(TAG, "final affinity: " + operand.getContactAffinity()); + } + + public float getContactAffinity() { + return mContactAffinity; + } + } } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 7a5336b..fd35ede 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -34,6 +34,7 @@ import android.database.ContentObserver; import android.media.AudioAttributes; import android.media.AudioManager; import android.net.Uri; +import android.os.Bundle; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings.Global; @@ -189,7 +190,7 @@ public class ZenModeHelper { } private boolean shouldInterceptAudience(NotificationRecord record) { - if (!audienceMatches(record)) { + if (!audienceMatches(record.getContactAffinity())) { ZenLog.traceIntercepted(record, "!audienceMatches"); return true; } @@ -372,14 +373,27 @@ public class ZenModeHelper { return record.isCategory(Notification.CATEGORY_MESSAGE) || isDefaultMessagingApp(record); } - private boolean audienceMatches(NotificationRecord record) { + public boolean matchesCallFilter(Bundle extras, ValidateNotificationPeople validator) { + final int zen = mZenMode; + if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through + if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) { + if (!mConfig.allowCalls) return false; // no calls get through + if (validator != null) { + final float contactAffinity = validator.getContactAffinity(extras); + return audienceMatches(contactAffinity); + } + } + return true; + } + + private boolean audienceMatches(float contactAffinity) { switch (mConfig.allowFrom) { case ZenModeConfig.SOURCE_ANYONE: return true; case ZenModeConfig.SOURCE_CONTACT: - return record.getContactAffinity() >= ValidateNotificationPeople.VALID_CONTACT; + return contactAffinity >= ValidateNotificationPeople.VALID_CONTACT; case ZenModeConfig.SOURCE_STAR: - return record.getContactAffinity() >= ValidateNotificationPeople.STARRED_CONTACT; + return contactAffinity >= ValidateNotificationPeople.STARRED_CONTACT; default: Slog.w(TAG, "Encountered unknown source: " + mConfig.allowFrom); return true; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 2cb9077..5aa0294 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -104,6 +104,7 @@ import android.content.pm.IPackageManager; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; +import android.content.pm.KeySet; import android.content.pm.ManifestDigest; import android.content.pm.PackageCleanItem; import android.content.pm.PackageInfo; @@ -7698,8 +7699,21 @@ public class PackageManagerService extends IPackageManager.Stub { public void installPackage(String originPath, IPackageInstallObserver2 observer, int installFlags, String installerPackageName, VerificationParams verificationParams, String packageAbiOverride) { + installPackageAsUser(originPath, observer, installFlags, installerPackageName, verificationParams, + packageAbiOverride, UserHandle.getCallingUserId()); + } + + @Override + public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer, + int installFlags, String installerPackageName, VerificationParams verificationParams, + String packageAbiOverride, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null); + if (UserHandle.getCallingUserId() != userId) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "installPackage " + userId); + } final File originFile = new File(originPath); final int uid = Binder.getCallingUid(); @@ -7717,7 +7731,7 @@ public class PackageManagerService extends IPackageManager.Stub { if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) { user = UserHandle.ALL; } else { - user = new UserHandle(UserHandle.getUserId(uid)); + user = new UserHandle(userId); } final int filteredInstallFlags; @@ -12965,7 +12979,7 @@ public class PackageManagerService extends IPackageManager.Stub { } /** Called by UserManagerService */ - void cleanUpUserLILPw(int userHandle) { + void cleanUpUserLILPw(UserManagerService userManager, int userHandle) { mDirtyUsers.remove(userHandle); mSettings.removeUserLPw(userHandle); mPendingBroadcasts.remove(userHandle); @@ -12976,6 +12990,50 @@ public class PackageManagerService extends IPackageManager.Stub { mInstaller.removeUserDataDirs(userHandle); } mUserNeedsBadging.delete(userHandle); + removeUnusedPackagesLILPw(userManager, userHandle); + } + + /** + * We're removing userHandle and would like to remove any downloaded packages + * that are no longer in use by any other user. + * @param userHandle the user being removed + */ + private void removeUnusedPackagesLILPw(UserManagerService userManager, final int userHandle) { + final boolean DEBUG_CLEAN_APKS = false; + int [] users = userManager.getUserIdsLPr(); + Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator(); + while (psit.hasNext()) { + PackageSetting ps = psit.next(); + final String packageName = ps.pkg.packageName; + // Skip over if system app + if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) { + continue; + } + if (DEBUG_CLEAN_APKS) { + Slog.i(TAG, "Checking package " + packageName); + } + boolean keep = false; + for (int i = 0; i < users.length; i++) { + if (users[i] != userHandle && ps.getInstalled(users[i])) { + keep = true; + if (DEBUG_CLEAN_APKS) { + Slog.i(TAG, " Keeping package " + packageName + " for user " + + users[i]); + } + break; + } + } + if (!keep) { + if (DEBUG_CLEAN_APKS) { + Slog.i(TAG, " Removing package " + packageName); + } + mHandler.post(new Runnable() { + public void run() { + deletePackageX(packageName, userHandle, 0); + } //end run + }); + } + } } /** Called by UserManagerService */ @@ -13075,7 +13133,7 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - public KeySetHandle getKeySetByAlias(String packageName, String alias) { + public KeySet getKeySetByAlias(String packageName, String alias) { if (packageName == null || alias == null) { return null; } @@ -13085,18 +13143,13 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "KeySet requested for unknown package:" + packageName); throw new IllegalArgumentException("Unknown package: " + packageName); } - if (pkg.applicationInfo.uid != Binder.getCallingUid() - && Process.SYSTEM_UID != Binder.getCallingUid()) { - throw new SecurityException("May not access KeySets defined by" - + " aliases in other applications."); - } KeySetManagerService ksms = mSettings.mKeySetManagerService; - return ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias); + return new KeySet(ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias)); } } @Override - public KeySetHandle getSigningKeySet(String packageName) { + public KeySet getSigningKeySet(String packageName) { if (packageName == null) { return null; } @@ -13111,12 +13164,12 @@ public class PackageManagerService extends IPackageManager.Stub { throw new SecurityException("May not access signing KeySet of other apps."); } KeySetManagerService ksms = mSettings.mKeySetManagerService; - return ksms.getSigningKeySetByPackageNameLPr(packageName); + return new KeySet(ksms.getSigningKeySetByPackageNameLPr(packageName)); } } @Override - public boolean isPackageSignedByKeySet(String packageName, IBinder ks) { + public boolean isPackageSignedByKeySet(String packageName, KeySet ks) { if (packageName == null || ks == null) { return false; } @@ -13126,16 +13179,17 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "KeySet requested for unknown package:" + packageName); throw new IllegalArgumentException("Unknown package: " + packageName); } - if (ks instanceof KeySetHandle) { + IBinder ksh = ks.getToken(); + if (ksh instanceof KeySetHandle) { KeySetManagerService ksms = mSettings.mKeySetManagerService; - return ksms.packageIsSignedByLPr(packageName, (KeySetHandle) ks); + return ksms.packageIsSignedByLPr(packageName, (KeySetHandle) ksh); } return false; } } @Override - public boolean isPackageSignedByKeySetExactly(String packageName, IBinder ks) { + public boolean isPackageSignedByKeySetExactly(String packageName, KeySet ks) { if (packageName == null || ks == null) { return false; } @@ -13145,9 +13199,10 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "KeySet requested for unknown package:" + packageName); throw new IllegalArgumentException("Unknown package: " + packageName); } - if (ks instanceof KeySetHandle) { + IBinder ksh = ks.getToken(); + if (ksh instanceof KeySetHandle) { KeySetManagerService ksms = mSettings.mKeySetManagerService; - return ksms.packageIsSignedByExactlyLPr(packageName, (KeySetHandle) ks); + return ksms.packageIsSignedByExactlyLPr(packageName, (KeySetHandle) ksh); } return false; } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 8ded7ca..2929939 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -293,6 +293,10 @@ public class UserManagerService extends IUserManager.Stub { private List<UserInfo> getProfilesLocked(int userId, boolean enabledOnly) { UserInfo user = getUserInfoLocked(userId); ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size()); + if (user == null) { + // Probably a dying user + return users; + } for (int i = 0; i < mUsers.size(); i++) { UserInfo profile = mUsers.valueAt(i); if (!isProfileOf(user, profile)) { @@ -1280,7 +1284,7 @@ public class UserManagerService extends IUserManager.Stub { private void removeUserStateLocked(final int userHandle) { // Cleanup package manager settings - mPm.cleanUpUserLILPw(userHandle); + mPm.cleanUpUserLILPw(this, userHandle); // Remove this user from the list mUsers.remove(userHandle); diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java index ef74205..69c9144 100644 --- a/services/core/java/com/android/server/wm/AppWindowAnimator.java +++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java @@ -114,6 +114,10 @@ public class AppWindowAnimator { transformation.clear(); transformation.setAlpha(mAppToken.isVisible() ? 1 : 0); hasTransformation = true; + + if (!mAppToken.appFullscreen) { + anim.setBackgroundColor(0); + } } public void setDummyAnimation() { diff --git a/telecomm/java/android/telecomm/PhoneAccount.java b/telecomm/java/android/telecomm/PhoneAccount.java index e8e6467..5b46409 100644 --- a/telecomm/java/android/telecomm/PhoneAccount.java +++ b/telecomm/java/android/telecomm/PhoneAccount.java @@ -94,7 +94,7 @@ public class PhoneAccount implements Parcelable { private CharSequence mLabel; private CharSequence mShortDescription; - private Builder() {} + public Builder() {} public Builder withAccountHandle(PhoneAccountHandle value) { this.mAccountHandle = value; diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java index 868282f..2243288 100644 --- a/telecomm/java/android/telecomm/TelecommManager.java +++ b/telecomm/java/android/telecomm/TelecommManager.java @@ -285,6 +285,20 @@ public class TelecommManager { } /** + * Sets the default account for making outgoing phone calls. + * @hide + */ + public void setDefaultOutgoingPhoneAccount(PhoneAccountHandle accountHandle) { + try { + if (isServiceConnected()) { + getTelecommService().setDefaultOutgoingPhoneAccount(accountHandle); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecommService#setDefaultOutgoingPhoneAccount"); + } + } + + /** * Return a list of {@link PhoneAccountHandle}s which can be used to make and receive phone * calls. * @@ -303,6 +317,55 @@ public class TelecommManager { } /** + * Returns the current SIM call manager. Apps must be prepared for this method to return + * {@code null}, indicating that there currently exists no user-chosen default + * {@code PhoneAccount}. + * @return The phone account handle of the current sim call manager. + * @hide + */ + public PhoneAccountHandle getSimCallManager() { + try { + if (isServiceConnected()) { + return getTelecommService().getSimCallManager(); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecommService#getSimCallManager"); + } + return null; + } + + /** + * Sets the SIM call manager to the specified phone account. + * @param accountHandle The phone account handle of the account to set as the sim call manager. + * @hide + */ + public void setSimCallManager(PhoneAccountHandle accountHandle) { + try { + if (isServiceConnected()) { + getTelecommService().setSimCallManager(accountHandle); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecommService#setSimCallManager"); + } + } + + /** + * Returns the list of registered SIM call managers. + * @return List of registered SIM call managers. + * @hide + */ + public List<PhoneAccountHandle> getSimCallManagers() { + try { + if (isServiceConnected()) { + return getTelecommService().getSimCallManagers(); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecommService#getSimCallManagers"); + } + return new ArrayList<>(); + } + + /** * Determine whether the device has more than one account registered and enabled. * * @return {@code true} if the device has more than one account registered and enabled and @@ -614,4 +677,4 @@ public class TelecommManager { } return isConnected; } -} +}
\ No newline at end of file diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl index 0ac5078..131307a 100644 --- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl +++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl @@ -35,37 +35,57 @@ interface ITelecommService { void showInCallScreen(boolean showDialpad); /** - * @see TelecommManager#getDefaultOutgoingPhoneAccount + * @see TelecommServiceImpl#getDefaultOutgoingPhoneAccount */ PhoneAccountHandle getDefaultOutgoingPhoneAccount(); /** - * @see TelecommManager#getOutgoingPhoneAccounts + * @see TelecommServiceImpl#setDefaultOutgoingPhoneAccount + */ + void setDefaultOutgoingPhoneAccount(in PhoneAccountHandle account); + + /** + * @see TelecommServiceImpl#getOutgoingPhoneAccounts */ List<PhoneAccountHandle> getOutgoingPhoneAccounts(); /** - * @see TelecommManager#getPhoneAccount + * @see TelecommServiceImpl#getPhoneAccount */ PhoneAccount getPhoneAccount(in PhoneAccountHandle account); /** - * @see TelecommManager#registerPhoneAccount + * @see TelecommServiceImpl#getSimCallManager + */ + PhoneAccountHandle getSimCallManager(); + + /** + * @see TelecommServiceImpl#setSimCallManager + */ + void setSimCallManager(in PhoneAccountHandle account); + + /** + * @see TelecommServiceImpl#getSimCallManagers + */ + List<PhoneAccountHandle> getSimCallManagers(); + + /** + * @see TelecommServiceImpl#registerPhoneAccount */ void registerPhoneAccount(in PhoneAccount metadata); /** - * @see TelecommManager#unregisterPhoneAccount + * @see TelecommServiceImpl#unregisterPhoneAccount */ void unregisterPhoneAccount(in PhoneAccountHandle account); /** - * @see TelecommManager#clearAccounts + * @see TelecommServiceImpl#clearAccounts */ void clearAccounts(String packageName); /** - * @see TelecommManager#getDefaultPhoneApp + * @see TelecommServiceImpl#getDefaultPhoneApp */ ComponentName getDefaultPhoneApp(); @@ -74,52 +94,52 @@ interface ITelecommService { // /** - * @see TelecommManager#silenceRinger + * @see TelecommServiceImpl#silenceRinger */ void silenceRinger(); /** - * @see TelecommManager#isInCall + * @see TelecommServiceImpl#isInCall */ boolean isInCall(); /** - * @see TelecomManager#isRinging + * @see TelecommServiceImpl#isRinging */ boolean isRinging(); /** - * @see TelecommManager#endCall + * @see TelecommServiceImpl#endCall */ boolean endCall(); /** - * @see TelecommManager#acceptRingingCall + * @see TelecommServiceImpl#acceptRingingCall */ void acceptRingingCall(); /** - * @see TelecommManager#cancelMissedCallsNotification + * @see TelecommServiceImpl#cancelMissedCallsNotification */ void cancelMissedCallsNotification(); /** - * @see TelecommManager#handleMmi + * @see TelecommServiceImpl#handleMmi */ boolean handlePinMmi(String dialString); /** - * @see TelecomManager#isTtySupported + * @see TelecommServiceImpl#isTtySupported */ boolean isTtySupported(); /** - * @see TelecomManager#getCurrentTtyMode + * @see TelecommServiceImpl#getCurrentTtyMode */ int getCurrentTtyMode(); /** - * @see TelecommManager#addNewIncomingCall + * @see TelecommServiceImpl#addNewIncomingCall */ void addNewIncomingCall(in PhoneAccountHandle phoneAccount, in Bundle extras); } diff --git a/telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl b/telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl index 42c77d7..0ab7564 100644 --- a/telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl +++ b/telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl @@ -20,6 +20,8 @@ import android.content.ComponentName; /** * Simple response callback object. + * + * {@hide} */ oneway interface RemoteServiceCallback { void onError(); diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index c84f40e..cd7d178 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -617,21 +617,25 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } + /** @hide */ @Override public KeySet getKeySetByAlias(String packageName, String alias) { throw new UnsupportedOperationException(); } + /** @hide */ @Override public KeySet getSigningKeySet(String packageName) { throw new UnsupportedOperationException(); } + /** @hide */ @Override public boolean isSignedBy(String packageName, KeySet ks) { throw new UnsupportedOperationException(); } + /** @hide */ @Override public boolean isSignedByExactly(String packageName, KeySet ks) { throw new UnsupportedOperationException(); diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java index b6591bd..c08c1a3 100644 --- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java +++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java @@ -23,7 +23,6 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.text.format.DateUtils; -import android.util.ArrayMap; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -36,6 +35,7 @@ import android.widget.TextView; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.Map; public class UsageStatsActivity extends ListActivity { private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14; @@ -84,7 +84,7 @@ public class UsageStatsActivity extends ListActivity { private void updateAdapter() { long now = System.currentTimeMillis(); long beginTime = now - USAGE_STATS_PERIOD; - ArrayMap<String, UsageStats> stats = mUsageStatsManager.queryAndAggregateUsageStats( + Map<String, UsageStats> stats = mUsageStatsManager.queryAndAggregateUsageStats( beginTime, now); mAdapter.update(stats); } @@ -92,17 +92,13 @@ public class UsageStatsActivity extends ListActivity { private class Adapter extends BaseAdapter { private ArrayList<UsageStats> mStats = new ArrayList<>(); - public void update(ArrayMap<String, UsageStats> stats) { + public void update(Map<String, UsageStats> stats) { mStats.clear(); if (stats == null) { return; } - final int packageCount = stats.size(); - for (int i = 0; i < packageCount; i++) { - mStats.add(stats.valueAt(i)); - } - + mStats.addAll(stats.values()); Collections.sort(mStats, mComparator); notifyDataSetChanged(); } diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index fd660bb..27e60f3 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -1038,9 +1038,9 @@ int doDump(Bundle* bundle) splitName.string()).string()); } - int32_t platformVersionCode = AaptXml::getIntegerAttribute(tree, NULL, - "platformBuildVersionCode"); - printf(" platformBuildVersionCode='%d'", platformVersionCode); + String8 platformVersionName = AaptXml::getAttribute(tree, NULL, + "platformBuildVersionName"); + printf(" platformBuildVersionName='%s'", platformVersionName.string()); printf("\n"); int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree, diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py index fce4323..6bb28e1 100644 --- a/tools/apilint/apilint.py +++ b/tools/apilint/apilint.py @@ -22,7 +22,7 @@ Usage: apilint.py current.txt Usage: apilint.py current.txt previous.txt """ -import re, sys +import re, sys, collections BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) @@ -149,6 +149,9 @@ def parse_api(fn): failures = [] +def filter_dupe(s): + return s.replace(" deprecated ", " ") + def _fail(clazz, detail, msg): """Records an API failure to be processed later.""" global failures @@ -158,7 +161,7 @@ def _fail(clazz, detail, msg): res += "\n in " + repr(detail) res += "\n in " + repr(clazz) res += "\n in " + repr(clazz.pkg) - failures.append(res) + failures.append(filter_dupe(res)) def warn(clazz, detail, msg): _fail(clazz, detail, "%sWarning:%s %s" % (format(fg=YELLOW, bg=BLACK), format(reset=True), msg)) @@ -393,16 +396,31 @@ def verify_intent_builder(clazz): def verify_helper_classes(clazz): - """Verify that helper classes are named consistently with what they extend.""" + """Verify that helper classes are named consistently with what they extend. + All developer extendable methods should be named onFoo().""" + test_methods = False if "extends android.app.Service" in clazz.raw: + test_methods = True if not clazz.name.endswith("Service"): error(clazz, None, "Inconsistent class name") if "extends android.content.ContentProvider" in clazz.raw: + test_methods = True if not clazz.name.endswith("Provider"): error(clazz, None, "Inconsistent class name") if "extends android.content.BroadcastReceiver" in clazz.raw: + test_methods = True if not clazz.name.endswith("Receiver"): error(clazz, None, "Inconsistent class name") + if "extends android.app.Activity" in clazz.raw: + test_methods = True + if not clazz.name.endswith("Activity"): + error(clazz, None, "Inconsistent class name") + + if test_methods: + for m in clazz.methods: + if "final" in m.split: continue + if not re.match("on[A-Z]", m.name): + error(clazz, m, "Extendable methods should be onFoo() style, otherwise final") def verify_builder(clazz): @@ -423,8 +441,12 @@ def verify_builder(clazz): if m.name.startswith("get"): continue if m.name.startswith("clear"): continue - if not m.typ.endswith(clazz.fullname): - warn(clazz, m, "Should return the builder") + if m.name.startswith("with"): + error(clazz, m, "Builder methods must be setFoo()") + + if m.name.startswith("set"): + if not m.typ.endswith(clazz.fullname): + warn(clazz, m, "Should return the builder") if not has_build: warn(clazz, None, "Missing build() method") @@ -486,6 +508,47 @@ def verify_layering(clazz): warn(clazz, m, "Method argument type violates package layering") +def verify_boolean(clazz): + """Catches people returning boolean from getFoo() style methods. + Ignores when matching setFoo() is present.""" + methods = [ m.name for m in clazz.methods ] + for m in clazz.methods: + if m.typ == "boolean" and m.name.startswith("get") and m.name != "get" and len(m.args) == 0: + setter = "set" + m.name[3:] + if setter not in methods: + error(clazz, m, "Methods returning boolean should be isFoo or hasFoo") + + +def verify_collections(clazz): + """Verifies that collection types are interfaces.""" + bad = ["java.util.Vector", "java.util.LinkedList", "java.util.ArrayList", "java.util.Stack", + "java.util.HashMap", "java.util.HashSet", "android.util.ArraySet", "android.util.ArrayMap"] + for m in clazz.methods: + filt = re.sub("<.+>", "", m.typ) + if filt in bad: + error(clazz, m, "Return type is concrete collection") + for arg in m.args: + filt = re.sub("<.+>", "", arg) + if filt in bad: + error(clazz, m, "Argument is concrete collection") + + +def verify_flags(clazz): + """Verifies that flags are non-overlapping.""" + known = collections.defaultdict(int) + for f in clazz.fields: + if "FLAG_" in f.name: + try: + val = int(f.value) + except: + continue + + scope = f.name[0:f.name.index("FLAG_")] + if val & known[scope]: + warn(clazz, f, "Found overlapping flag") + known[scope] |= val + + def verify_all(api): global failures @@ -518,6 +581,9 @@ def verify_all(api): verify_aidl(clazz) verify_internal(clazz) verify_layering(clazz) + verify_boolean(clazz) + verify_collections(clazz) + verify_flags(clazz) return failures |