Merge "Fix a memory leak in ConnectivityManager." into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 3444597..eb129da 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3578,6 +3578,7 @@
method public final deprecated void removeDialog(int);
method public void reportFullyDrawn();
method public android.view.DropPermissions requestDropPermissions(android.view.DragEvent);
+ method public final void requestKeyboardShortcutsHelper();
method public final void requestPermissions(java.lang.String[], int);
method public boolean requestVisibleBehind(boolean);
method public final boolean requestWindowFeature(int);
@@ -5030,13 +5031,13 @@
method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
method public java.lang.CharSequence getCancelLabel();
method public java.lang.CharSequence getConfirmLabel();
- method public boolean getHintContentIntentLaunchesActivity();
+ method public boolean getHintLaunchesActivity();
method public java.lang.CharSequence getInProgressLabel();
method public boolean isAvailableOffline();
method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
- method public android.app.Notification.Action.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+ method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean);
method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
}
@@ -9496,10 +9497,8 @@
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
- method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
- method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo, android.os.UserHandle);
- method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
- method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
+ method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo);
+ method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle);
method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
method public boolean hasShortcutHostPermission();
method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
@@ -9511,6 +9510,7 @@
method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public boolean startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
+ method public boolean startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
}
@@ -9532,6 +9532,7 @@
method public void setChangedSince(long);
method public void setPackage(java.lang.String);
method public void setQueryFlags(int);
+ method public void setShortcutIds(java.util.List<java.lang.String>);
field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
field public static final int FLAG_GET_PINNED = 2; // 0x2
@@ -10038,13 +10039,16 @@
public final class ShortcutInfo implements android.os.Parcelable {
method public int describeContents();
method public android.content.ComponentName getActivityComponent();
+ method public java.util.List<java.lang.String> getCategories();
method public android.os.PersistableBundle getExtras();
+ method public int getIconResourceId();
method public java.lang.String getId();
method public android.content.Intent getIntent();
method public long getLastChangedTimestamp();
method public java.lang.String getPackageName();
method public java.lang.String getText();
method public java.lang.String getTitle();
+ method public android.os.UserHandle getUserHandle();
method public int getWeight();
method public boolean hasIconFile();
method public boolean hasIconResource();
@@ -10061,12 +10065,14 @@
field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
field public static final int FLAG_PINNED = 2; // 0x2
+ field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
}
public static class ShortcutInfo.Builder {
ctor public ShortcutInfo.Builder(android.content.Context);
method public android.content.pm.ShortcutInfo build();
method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName);
+ method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.List<java.lang.String>);
method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
@@ -10077,15 +10083,15 @@
}
public class ShortcutManager {
- method public boolean addDynamicShortcut(android.content.pm.ShortcutInfo);
- method public void deleteAllDynamicShortcuts();
- method public void deleteDynamicShortcut(java.lang.String);
+ method public boolean addDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
method public int getIconMaxDimensions();
method public int getMaxDynamicShortcutCount();
method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
method public long getRateLimitResetTime();
method public int getRemainingCallCount();
+ method public void removeAllDynamicShortcuts();
+ method public void removeDynamicShortcuts(java.util.List<java.lang.String>);
method public boolean setDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
method public boolean updateShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
}
@@ -14431,13 +14437,13 @@
public final class OutputConfiguration implements android.os.Parcelable {
ctor public OutputConfiguration(android.view.Surface);
+ ctor public OutputConfiguration(int, android.view.Surface);
method public int describeContents();
method public android.view.Surface getSurface();
- method public int getSurfaceSetId();
- method public void setSurfaceSetId(int);
+ method public int getSurfaceGroupId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
- field public static final int SURFACE_SET_ID_INVALID = -1; // 0xffffffff
+ field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
}
public final class RggbChannelVector {
@@ -19852,8 +19858,8 @@
public class AudioRecord implements android.media.AudioRouting {
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
- method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getAudioSource();
@@ -19878,8 +19884,8 @@
method public int read(java.nio.ByteBuffer, int);
method public int read(java.nio.ByteBuffer, int, int);
method public void release();
+ method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public deprecated void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
- method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
method public int setNotificationMarkerPosition(int);
method public int setPositionNotificationPeriod(int);
method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
@@ -19913,8 +19919,9 @@
method public abstract void onPeriodicNotification(android.media.AudioRecord);
}
- public static abstract interface AudioRecord.OnRoutingChangedListener {
+ public static abstract deprecated interface AudioRecord.OnRoutingChangedListener implements android.media.AudioRouting.OnRoutingChangedListener {
method public abstract void onRoutingChanged(android.media.AudioRecord);
+ method public default void onRoutingChanged(android.media.AudioRouting);
}
public final class AudioRecordingConfiguration implements android.os.Parcelable {
@@ -19929,10 +19936,10 @@
}
public abstract interface AudioRouting {
- method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
+ method public abstract void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public abstract android.media.AudioDeviceInfo getPreferredDevice();
method public abstract android.media.AudioDeviceInfo getRoutedDevice();
- method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
+ method public abstract void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
}
@@ -19952,8 +19959,8 @@
ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public deprecated void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
- method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public int attachAuxEffect(int);
method public void flush();
method public int getAudioFormat();
@@ -19985,8 +19992,8 @@
method public void play() throws java.lang.IllegalStateException;
method public void release();
method public int reloadStaticData();
+ method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
- method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
method public int setAuxEffectSendLevel(float);
method public int setBufferSizeInFrames(int);
method public int setLoopPoints(int, int, int);
@@ -20040,8 +20047,9 @@
method public abstract void onPeriodicNotification(android.media.AudioTrack);
}
- public static abstract deprecated interface AudioTrack.OnRoutingChangedListener {
- method public abstract deprecated void onRoutingChanged(android.media.AudioTrack);
+ public static abstract deprecated interface AudioTrack.OnRoutingChangedListener implements android.media.AudioRouting.OnRoutingChangedListener {
+ method public abstract void onRoutingChanged(android.media.AudioTrack);
+ method public default void onRoutingChanged(android.media.AudioRouting);
}
public class CamcorderProfile {
@@ -20717,10 +20725,12 @@
field public static final int VP9Level6 = 1024; // 0x400
field public static final int VP9Level61 = 2048; // 0x800
field public static final int VP9Level62 = 4096; // 0x1000
- field public static final int VP9Profile0 = 0; // 0x0
- field public static final int VP9Profile1 = 1; // 0x1
- field public static final int VP9Profile2 = 2; // 0x2
- field public static final int VP9Profile3 = 3; // 0x3
+ field public static final int VP9Profile0 = 1; // 0x1
+ field public static final int VP9Profile1 = 2; // 0x2
+ field public static final int VP9Profile2 = 4; // 0x4
+ field public static final int VP9Profile2HDR = 4096; // 0x1000
+ field public static final int VP9Profile3 = 8; // 0x8
+ field public static final int VP9Profile3HDR = 8192; // 0x2000
field public int level;
field public int profile;
}
@@ -22957,6 +22967,7 @@
method public android.content.Intent createSettingsIntent();
method public android.content.Intent createSetupIntent();
method public int describeContents();
+ method public android.os.Bundle getExtras();
method public java.lang.String getId();
method public java.lang.String getParentId();
method public android.content.pm.ServiceInfo getServiceInfo();
@@ -22986,6 +22997,7 @@
ctor public TvInputInfo.Builder(android.content.Context, android.content.ComponentName);
method public android.media.tv.TvInputInfo build() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean);
+ method public android.media.tv.TvInputInfo.Builder setExtras(android.os.Bundle);
method public android.media.tv.TvInputInfo.Builder setTunerCount(int);
}
@@ -29575,6 +29587,7 @@
public class StorageManager {
method public java.lang.String getMountedObbPath(java.lang.String);
method public android.os.storage.StorageVolume getPrimaryStorageVolume();
+ method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
method public boolean isEncrypted(java.io.File);
method public boolean isObbMounted(java.lang.String);
@@ -49052,6 +49065,7 @@
ctor public BufferedReader(java.io.Reader, int);
ctor public BufferedReader(java.io.Reader);
method public void close() throws java.io.IOException;
+ method public java.util.stream.Stream<java.lang.String> lines();
method public int read(char[], int, int) throws java.io.IOException;
method public java.lang.String readLine() throws java.io.IOException;
}
@@ -49922,6 +49936,11 @@
ctor public UTFDataFormatException(java.lang.String);
}
+ public class UncheckedIOException extends java.lang.RuntimeException {
+ ctor public UncheckedIOException(java.lang.String, java.io.IOException);
+ ctor public UncheckedIOException(java.io.IOException);
+ }
+
public class UnsupportedEncodingException extends java.io.IOException {
ctor public UnsupportedEncodingException();
ctor public UnsupportedEncodingException(java.lang.String);
@@ -57578,6 +57597,7 @@
method public void set(int, int);
method public void set(int, int, boolean);
method public int size();
+ method public java.util.stream.IntStream stream();
method public byte[] toByteArray();
method public long[] toLongArray();
method public static java.util.BitSet valueOf(long[]);
@@ -58033,6 +58053,7 @@
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
method public boolean replace(K, V, V);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
}
public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
@@ -58076,6 +58097,7 @@
method public synchronized boolean remove(java.lang.Object, java.lang.Object);
method public synchronized boolean replace(K, V, V);
method public synchronized V replace(K, V);
+ method public synchronized void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public synchronized int size();
method public java.util.Collection<V> values();
}
@@ -58616,6 +58638,18 @@
public class Random implements java.io.Serializable {
ctor public Random();
ctor public Random(long);
+ method public java.util.stream.DoubleStream doubles(long);
+ method public java.util.stream.DoubleStream doubles();
+ method public java.util.stream.DoubleStream doubles(long, double, double);
+ method public java.util.stream.DoubleStream doubles(double, double);
+ method public java.util.stream.IntStream ints(long);
+ method public java.util.stream.IntStream ints();
+ method public java.util.stream.IntStream ints(long, int, int);
+ method public java.util.stream.IntStream ints(int, int);
+ method public java.util.stream.LongStream longs(long);
+ method public java.util.stream.LongStream longs();
+ method public java.util.stream.LongStream longs(long, long, long);
+ method public java.util.stream.LongStream longs(long, long);
method protected int next(int);
method public boolean nextBoolean();
method public void nextBytes(byte[]);
@@ -59057,6 +59091,7 @@
method public java.util.NavigableSet<K> navigableKeySet();
method public java.util.Map.Entry<K, V> pollFirstEntry();
method public java.util.Map.Entry<K, V> pollLastEntry();
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public java.util.NavigableMap<K, V> subMap(K, boolean, K, boolean);
method public java.util.SortedMap<K, V> subMap(K, K);
method public java.util.NavigableMap<K, V> tailMap(K, boolean);
@@ -59158,6 +59193,7 @@
ctor public WeakHashMap(java.util.Map<? extends K, ? extends V>);
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
}
}
@@ -60131,18 +60167,6 @@
public class ThreadLocalRandom extends java.util.Random {
method public static java.util.concurrent.ThreadLocalRandom current();
- method public java.util.stream.DoubleStream doubles(long);
- method public java.util.stream.DoubleStream doubles();
- method public java.util.stream.DoubleStream doubles(long, double, double);
- method public java.util.stream.DoubleStream doubles(double, double);
- method public java.util.stream.IntStream ints(long);
- method public java.util.stream.IntStream ints();
- method public java.util.stream.IntStream ints(long, int, int);
- method public java.util.stream.IntStream ints(int, int);
- method public java.util.stream.LongStream longs(long);
- method public java.util.stream.LongStream longs();
- method public java.util.stream.LongStream longs(long, long, long);
- method public java.util.stream.LongStream longs(long, long);
method public double nextDouble(double);
method public double nextDouble(double, double);
method public int nextInt(int, int);
@@ -61515,6 +61539,7 @@
}
public final class Pattern implements java.io.Serializable {
+ method public java.util.function.Predicate<java.lang.String> asPredicate();
method public static java.util.regex.Pattern compile(java.lang.String);
method public static java.util.regex.Pattern compile(java.lang.String, int) throws java.util.regex.PatternSyntaxException;
method public int flags();
@@ -61524,6 +61549,7 @@
method public static java.lang.String quote(java.lang.String);
method public java.lang.String[] split(java.lang.CharSequence, int);
method public java.lang.String[] split(java.lang.CharSequence);
+ method public java.util.stream.Stream<java.lang.String> splitAsStream(java.lang.CharSequence);
field public static final int CANON_EQ = 128; // 0x80
field public static final int CASE_INSENSITIVE = 2; // 0x2
field public static final int COMMENTS = 4; // 0x4
diff --git a/api/system-current.txt b/api/system-current.txt
index a4927f2..5bfbc16 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3696,6 +3696,7 @@
method public final deprecated void removeDialog(int);
method public void reportFullyDrawn();
method public android.view.DropPermissions requestDropPermissions(android.view.DragEvent);
+ method public final void requestKeyboardShortcutsHelper();
method public final void requestPermissions(java.lang.String[], int);
method public boolean requestVisibleBehind(boolean);
method public final boolean requestWindowFeature(int);
@@ -5164,13 +5165,13 @@
method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
method public java.lang.CharSequence getCancelLabel();
method public java.lang.CharSequence getConfirmLabel();
- method public boolean getHintContentIntentLaunchesActivity();
+ method public boolean getHintLaunchesActivity();
method public java.lang.CharSequence getInProgressLabel();
method public boolean isAvailableOffline();
method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
- method public android.app.Notification.Action.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+ method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean);
method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
}
@@ -9834,10 +9835,8 @@
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
- method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
- method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo, android.os.UserHandle);
- method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
- method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
+ method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo);
+ method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle);
method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
method public boolean hasShortcutHostPermission();
method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
@@ -9849,6 +9848,7 @@
method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public boolean startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
+ method public boolean startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
}
@@ -9870,6 +9870,7 @@
method public void setChangedSince(long);
method public void setPackage(java.lang.String);
method public void setQueryFlags(int);
+ method public void setShortcutIds(java.util.List<java.lang.String>);
field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
field public static final int FLAG_GET_PINNED = 2; // 0x2
@@ -10438,13 +10439,16 @@
public final class ShortcutInfo implements android.os.Parcelable {
method public int describeContents();
method public android.content.ComponentName getActivityComponent();
+ method public java.util.List<java.lang.String> getCategories();
method public android.os.PersistableBundle getExtras();
+ method public int getIconResourceId();
method public java.lang.String getId();
method public android.content.Intent getIntent();
method public long getLastChangedTimestamp();
method public java.lang.String getPackageName();
method public java.lang.String getText();
method public java.lang.String getTitle();
+ method public android.os.UserHandle getUserHandle();
method public int getWeight();
method public boolean hasIconFile();
method public boolean hasIconResource();
@@ -10461,12 +10465,14 @@
field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
field public static final int FLAG_PINNED = 2; // 0x2
+ field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
}
public static class ShortcutInfo.Builder {
ctor public ShortcutInfo.Builder(android.content.Context);
method public android.content.pm.ShortcutInfo build();
method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName);
+ method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.List<java.lang.String>);
method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
@@ -10477,15 +10483,15 @@
}
public class ShortcutManager {
- method public boolean addDynamicShortcut(android.content.pm.ShortcutInfo);
- method public void deleteAllDynamicShortcuts();
- method public void deleteDynamicShortcut(java.lang.String);
+ method public boolean addDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
method public int getIconMaxDimensions();
method public int getMaxDynamicShortcutCount();
method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
method public long getRateLimitResetTime();
method public int getRemainingCallCount();
+ method public void removeAllDynamicShortcuts();
+ method public void removeDynamicShortcuts(java.util.List<java.lang.String>);
method public boolean setDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
method public boolean updateShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
}
@@ -14839,20 +14845,20 @@
public final class OutputConfiguration implements android.os.Parcelable {
ctor public OutputConfiguration(android.view.Surface);
+ ctor public OutputConfiguration(int, android.view.Surface);
ctor public OutputConfiguration(android.view.Surface, int);
- ctor public OutputConfiguration(android.hardware.camera2.params.OutputConfiguration);
+ ctor public OutputConfiguration(int, android.view.Surface, int);
method public int describeContents();
method public int getRotation();
method public android.view.Surface getSurface();
- method public int getSurfaceSetId();
- method public void setSurfaceSetId(int);
+ method public int getSurfaceGroupId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
field public static final int ROTATION_0 = 0; // 0x0
field public static final int ROTATION_180 = 2; // 0x2
field public static final int ROTATION_270 = 3; // 0x3
field public static final int ROTATION_90 = 1; // 0x1
- field public static final int SURFACE_SET_ID_INVALID = -1; // 0xffffffff
+ field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
}
public final class RggbChannelVector {
@@ -15490,6 +15496,7 @@
public class NanoApp {
ctor public NanoApp();
+ ctor public NanoApp(int, byte[]);
method public int describeContents();
method public byte[] getAppBinary();
method public int getAppId();
@@ -21330,8 +21337,8 @@
public class AudioRecord implements android.media.AudioRouting {
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
- method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getAudioSource();
@@ -21356,8 +21363,8 @@
method public int read(java.nio.ByteBuffer, int);
method public int read(java.nio.ByteBuffer, int, int);
method public void release();
+ method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public deprecated void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
- method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
method public int setNotificationMarkerPosition(int);
method public int setPositionNotificationPeriod(int);
method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
@@ -21393,8 +21400,9 @@
method public abstract void onPeriodicNotification(android.media.AudioRecord);
}
- public static abstract interface AudioRecord.OnRoutingChangedListener {
+ public static abstract deprecated interface AudioRecord.OnRoutingChangedListener implements android.media.AudioRouting.OnRoutingChangedListener {
method public abstract void onRoutingChanged(android.media.AudioRecord);
+ method public default void onRoutingChanged(android.media.AudioRouting);
}
public final class AudioRecordingConfiguration implements android.os.Parcelable {
@@ -21409,10 +21417,10 @@
}
public abstract interface AudioRouting {
- method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
+ method public abstract void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public abstract android.media.AudioDeviceInfo getPreferredDevice();
method public abstract android.media.AudioDeviceInfo getRoutedDevice();
- method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
+ method public abstract void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
}
@@ -21432,8 +21440,8 @@
ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public deprecated void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
- method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public int attachAuxEffect(int);
method public void flush();
method public int getAudioFormat();
@@ -21465,8 +21473,8 @@
method public void play() throws java.lang.IllegalStateException;
method public void release();
method public int reloadStaticData();
+ method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
- method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
method public int setAuxEffectSendLevel(float);
method public int setBufferSizeInFrames(int);
method public int setLoopPoints(int, int, int);
@@ -21520,8 +21528,9 @@
method public abstract void onPeriodicNotification(android.media.AudioTrack);
}
- public static abstract deprecated interface AudioTrack.OnRoutingChangedListener {
- method public abstract deprecated void onRoutingChanged(android.media.AudioTrack);
+ public static abstract deprecated interface AudioTrack.OnRoutingChangedListener implements android.media.AudioRouting.OnRoutingChangedListener {
+ method public abstract void onRoutingChanged(android.media.AudioTrack);
+ method public default void onRoutingChanged(android.media.AudioRouting);
}
public class CamcorderProfile {
@@ -22197,10 +22206,12 @@
field public static final int VP9Level6 = 1024; // 0x400
field public static final int VP9Level61 = 2048; // 0x800
field public static final int VP9Level62 = 4096; // 0x1000
- field public static final int VP9Profile0 = 0; // 0x0
- field public static final int VP9Profile1 = 1; // 0x1
- field public static final int VP9Profile2 = 2; // 0x2
- field public static final int VP9Profile3 = 3; // 0x3
+ field public static final int VP9Profile0 = 1; // 0x1
+ field public static final int VP9Profile1 = 2; // 0x2
+ field public static final int VP9Profile2 = 4; // 0x4
+ field public static final int VP9Profile2HDR = 4096; // 0x1000
+ field public static final int VP9Profile3 = 8; // 0x8
+ field public static final int VP9Profile3HDR = 8192; // 0x2000
field public int level;
field public int profile;
}
@@ -24613,6 +24624,7 @@
method public static deprecated android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.media.tv.TvInputHardwareInfo, java.lang.String, android.net.Uri) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public static deprecated android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.media.tv.TvInputHardwareInfo, int, android.graphics.drawable.Icon) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public int describeContents();
+ method public android.os.Bundle getExtras();
method public android.hardware.hdmi.HdmiDeviceInfo getHdmiDeviceInfo();
method public java.lang.String getId();
method public java.lang.String getParentId();
@@ -24646,6 +24658,7 @@
ctor public TvInputInfo.Builder(android.content.Context, android.content.ComponentName);
method public android.media.tv.TvInputInfo build() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean);
+ method public android.media.tv.TvInputInfo.Builder setExtras(android.os.Bundle);
method public android.media.tv.TvInputInfo.Builder setHdmiDeviceInfo(android.hardware.hdmi.HdmiDeviceInfo);
method public android.media.tv.TvInputInfo.Builder setIcon(android.graphics.drawable.Icon);
method public android.media.tv.TvInputInfo.Builder setIcon(android.graphics.drawable.Icon, int);
@@ -31880,6 +31893,7 @@
public class StorageManager {
method public java.lang.String getMountedObbPath(java.lang.String);
method public android.os.storage.StorageVolume getPrimaryStorageVolume();
+ method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
method public boolean isEncrypted(java.io.File);
method public boolean isObbMounted(java.lang.String);
@@ -52164,6 +52178,7 @@
ctor public BufferedReader(java.io.Reader, int);
ctor public BufferedReader(java.io.Reader);
method public void close() throws java.io.IOException;
+ method public java.util.stream.Stream<java.lang.String> lines();
method public int read(char[], int, int) throws java.io.IOException;
method public java.lang.String readLine() throws java.io.IOException;
}
@@ -53034,6 +53049,11 @@
ctor public UTFDataFormatException(java.lang.String);
}
+ public class UncheckedIOException extends java.lang.RuntimeException {
+ ctor public UncheckedIOException(java.lang.String, java.io.IOException);
+ ctor public UncheckedIOException(java.io.IOException);
+ }
+
public class UnsupportedEncodingException extends java.io.IOException {
ctor public UnsupportedEncodingException();
ctor public UnsupportedEncodingException(java.lang.String);
@@ -60690,6 +60710,7 @@
method public void set(int, int);
method public void set(int, int, boolean);
method public int size();
+ method public java.util.stream.IntStream stream();
method public byte[] toByteArray();
method public long[] toLongArray();
method public static java.util.BitSet valueOf(long[]);
@@ -61145,6 +61166,7 @@
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
method public boolean replace(K, V, V);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
}
public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
@@ -61188,6 +61210,7 @@
method public synchronized boolean remove(java.lang.Object, java.lang.Object);
method public synchronized boolean replace(K, V, V);
method public synchronized V replace(K, V);
+ method public synchronized void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public synchronized int size();
method public java.util.Collection<V> values();
}
@@ -61728,6 +61751,18 @@
public class Random implements java.io.Serializable {
ctor public Random();
ctor public Random(long);
+ method public java.util.stream.DoubleStream doubles(long);
+ method public java.util.stream.DoubleStream doubles();
+ method public java.util.stream.DoubleStream doubles(long, double, double);
+ method public java.util.stream.DoubleStream doubles(double, double);
+ method public java.util.stream.IntStream ints(long);
+ method public java.util.stream.IntStream ints();
+ method public java.util.stream.IntStream ints(long, int, int);
+ method public java.util.stream.IntStream ints(int, int);
+ method public java.util.stream.LongStream longs(long);
+ method public java.util.stream.LongStream longs();
+ method public java.util.stream.LongStream longs(long, long, long);
+ method public java.util.stream.LongStream longs(long, long);
method protected int next(int);
method public boolean nextBoolean();
method public void nextBytes(byte[]);
@@ -62169,6 +62204,7 @@
method public java.util.NavigableSet<K> navigableKeySet();
method public java.util.Map.Entry<K, V> pollFirstEntry();
method public java.util.Map.Entry<K, V> pollLastEntry();
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public java.util.NavigableMap<K, V> subMap(K, boolean, K, boolean);
method public java.util.SortedMap<K, V> subMap(K, K);
method public java.util.NavigableMap<K, V> tailMap(K, boolean);
@@ -62270,6 +62306,7 @@
ctor public WeakHashMap(java.util.Map<? extends K, ? extends V>);
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
}
}
@@ -63243,18 +63280,6 @@
public class ThreadLocalRandom extends java.util.Random {
method public static java.util.concurrent.ThreadLocalRandom current();
- method public java.util.stream.DoubleStream doubles(long);
- method public java.util.stream.DoubleStream doubles();
- method public java.util.stream.DoubleStream doubles(long, double, double);
- method public java.util.stream.DoubleStream doubles(double, double);
- method public java.util.stream.IntStream ints(long);
- method public java.util.stream.IntStream ints();
- method public java.util.stream.IntStream ints(long, int, int);
- method public java.util.stream.IntStream ints(int, int);
- method public java.util.stream.LongStream longs(long);
- method public java.util.stream.LongStream longs();
- method public java.util.stream.LongStream longs(long, long, long);
- method public java.util.stream.LongStream longs(long, long);
method public double nextDouble(double);
method public double nextDouble(double, double);
method public int nextInt(int, int);
@@ -64627,6 +64652,7 @@
}
public final class Pattern implements java.io.Serializable {
+ method public java.util.function.Predicate<java.lang.String> asPredicate();
method public static java.util.regex.Pattern compile(java.lang.String);
method public static java.util.regex.Pattern compile(java.lang.String, int) throws java.util.regex.PatternSyntaxException;
method public int flags();
@@ -64636,6 +64662,7 @@
method public static java.lang.String quote(java.lang.String);
method public java.lang.String[] split(java.lang.CharSequence, int);
method public java.lang.String[] split(java.lang.CharSequence);
+ method public java.util.stream.Stream<java.lang.String> splitAsStream(java.lang.CharSequence);
field public static final int CANON_EQ = 128; // 0x80
field public static final int CASE_INSENSITIVE = 2; // 0x2
field public static final int COMMENTS = 4; // 0x4
diff --git a/api/test-current.txt b/api/test-current.txt
index 4179948..04fedb1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3578,6 +3578,7 @@
method public final deprecated void removeDialog(int);
method public void reportFullyDrawn();
method public android.view.DropPermissions requestDropPermissions(android.view.DragEvent);
+ method public final void requestKeyboardShortcutsHelper();
method public final void requestPermissions(java.lang.String[], int);
method public boolean requestVisibleBehind(boolean);
method public final boolean requestWindowFeature(int);
@@ -5030,13 +5031,13 @@
method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
method public java.lang.CharSequence getCancelLabel();
method public java.lang.CharSequence getConfirmLabel();
- method public boolean getHintContentIntentLaunchesActivity();
+ method public boolean getHintLaunchesActivity();
method public java.lang.CharSequence getInProgressLabel();
method public boolean isAvailableOffline();
method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
- method public android.app.Notification.Action.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+ method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean);
method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
}
@@ -9504,11 +9505,10 @@
}
public class LauncherApps {
+ ctor public LauncherApps(android.content.Context);
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
- method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
- method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo, android.os.UserHandle);
- method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
- method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
+ method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo);
+ method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle);
method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
method public boolean hasShortcutHostPermission();
method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
@@ -9520,6 +9520,7 @@
method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public boolean startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
+ method public boolean startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
}
@@ -9541,6 +9542,7 @@
method public void setChangedSince(long);
method public void setPackage(java.lang.String);
method public void setQueryFlags(int);
+ method public void setShortcutIds(java.util.List<java.lang.String>);
field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
field public static final int FLAG_GET_PINNED = 2; // 0x2
@@ -10048,13 +10050,16 @@
public final class ShortcutInfo implements android.os.Parcelable {
method public int describeContents();
method public android.content.ComponentName getActivityComponent();
+ method public java.util.List<java.lang.String> getCategories();
method public android.os.PersistableBundle getExtras();
+ method public int getIconResourceId();
method public java.lang.String getId();
method public android.content.Intent getIntent();
method public long getLastChangedTimestamp();
method public java.lang.String getPackageName();
method public java.lang.String getText();
method public java.lang.String getTitle();
+ method public android.os.UserHandle getUserHandle();
method public int getWeight();
method public boolean hasIconFile();
method public boolean hasIconResource();
@@ -10071,12 +10076,14 @@
field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
field public static final int FLAG_PINNED = 2; // 0x2
+ field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
}
public static class ShortcutInfo.Builder {
ctor public ShortcutInfo.Builder(android.content.Context);
method public android.content.pm.ShortcutInfo build();
method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName);
+ method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.List<java.lang.String>);
method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
@@ -10087,15 +10094,16 @@
}
public class ShortcutManager {
- method public boolean addDynamicShortcut(android.content.pm.ShortcutInfo);
- method public void deleteAllDynamicShortcuts();
- method public void deleteDynamicShortcut(java.lang.String);
+ ctor public ShortcutManager(android.content.Context);
+ method public boolean addDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
method public int getIconMaxDimensions();
method public int getMaxDynamicShortcutCount();
method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
method public long getRateLimitResetTime();
method public int getRemainingCallCount();
+ method public void removeAllDynamicShortcuts();
+ method public void removeDynamicShortcuts(java.util.List<java.lang.String>);
method public boolean setDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
method public boolean updateShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
}
@@ -14441,13 +14449,13 @@
public final class OutputConfiguration implements android.os.Parcelable {
ctor public OutputConfiguration(android.view.Surface);
+ ctor public OutputConfiguration(int, android.view.Surface);
method public int describeContents();
method public android.view.Surface getSurface();
- method public int getSurfaceSetId();
- method public void setSurfaceSetId(int);
+ method public int getSurfaceGroupId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
- field public static final int SURFACE_SET_ID_INVALID = -1; // 0xffffffff
+ field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
}
public final class RggbChannelVector {
@@ -19918,8 +19926,8 @@
public class AudioRecord implements android.media.AudioRouting {
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
- method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getAudioSource();
@@ -19944,8 +19952,8 @@
method public int read(java.nio.ByteBuffer, int);
method public int read(java.nio.ByteBuffer, int, int);
method public void release();
+ method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public deprecated void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
- method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
method public int setNotificationMarkerPosition(int);
method public int setPositionNotificationPeriod(int);
method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
@@ -19979,8 +19987,9 @@
method public abstract void onPeriodicNotification(android.media.AudioRecord);
}
- public static abstract interface AudioRecord.OnRoutingChangedListener {
+ public static abstract deprecated interface AudioRecord.OnRoutingChangedListener implements android.media.AudioRouting.OnRoutingChangedListener {
method public abstract void onRoutingChanged(android.media.AudioRecord);
+ method public default void onRoutingChanged(android.media.AudioRouting);
}
public final class AudioRecordingConfiguration implements android.os.Parcelable {
@@ -19995,10 +20004,10 @@
}
public abstract interface AudioRouting {
- method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
+ method public abstract void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public abstract android.media.AudioDeviceInfo getPreferredDevice();
method public abstract android.media.AudioDeviceInfo getRoutedDevice();
- method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
+ method public abstract void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
}
@@ -20018,8 +20027,8 @@
ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public deprecated void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
- method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public int attachAuxEffect(int);
method public void flush();
method public int getAudioFormat();
@@ -20051,8 +20060,8 @@
method public void play() throws java.lang.IllegalStateException;
method public void release();
method public int reloadStaticData();
+ method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
- method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
method public int setAuxEffectSendLevel(float);
method public int setBufferSizeInFrames(int);
method public int setLoopPoints(int, int, int);
@@ -20106,8 +20115,9 @@
method public abstract void onPeriodicNotification(android.media.AudioTrack);
}
- public static abstract deprecated interface AudioTrack.OnRoutingChangedListener {
- method public abstract deprecated void onRoutingChanged(android.media.AudioTrack);
+ public static abstract deprecated interface AudioTrack.OnRoutingChangedListener implements android.media.AudioRouting.OnRoutingChangedListener {
+ method public abstract void onRoutingChanged(android.media.AudioTrack);
+ method public default void onRoutingChanged(android.media.AudioRouting);
}
public class CamcorderProfile {
@@ -20783,10 +20793,12 @@
field public static final int VP9Level6 = 1024; // 0x400
field public static final int VP9Level61 = 2048; // 0x800
field public static final int VP9Level62 = 4096; // 0x1000
- field public static final int VP9Profile0 = 0; // 0x0
- field public static final int VP9Profile1 = 1; // 0x1
- field public static final int VP9Profile2 = 2; // 0x2
- field public static final int VP9Profile3 = 3; // 0x3
+ field public static final int VP9Profile0 = 1; // 0x1
+ field public static final int VP9Profile1 = 2; // 0x2
+ field public static final int VP9Profile2 = 4; // 0x4
+ field public static final int VP9Profile2HDR = 4096; // 0x1000
+ field public static final int VP9Profile3 = 8; // 0x8
+ field public static final int VP9Profile3HDR = 8192; // 0x2000
field public int level;
field public int profile;
}
@@ -23023,6 +23035,7 @@
method public android.content.Intent createSettingsIntent();
method public android.content.Intent createSetupIntent();
method public int describeContents();
+ method public android.os.Bundle getExtras();
method public java.lang.String getId();
method public java.lang.String getParentId();
method public android.content.pm.ServiceInfo getServiceInfo();
@@ -23052,6 +23065,7 @@
ctor public TvInputInfo.Builder(android.content.Context, android.content.ComponentName);
method public android.media.tv.TvInputInfo build() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean);
+ method public android.media.tv.TvInputInfo.Builder setExtras(android.os.Bundle);
method public android.media.tv.TvInputInfo.Builder setTunerCount(int);
}
@@ -29642,6 +29656,7 @@
public class StorageManager {
method public java.lang.String getMountedObbPath(java.lang.String);
method public android.os.storage.StorageVolume getPrimaryStorageVolume();
+ method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
method public boolean isEncrypted(java.io.File);
method public boolean isObbMounted(java.lang.String);
@@ -49128,6 +49143,7 @@
ctor public BufferedReader(java.io.Reader, int);
ctor public BufferedReader(java.io.Reader);
method public void close() throws java.io.IOException;
+ method public java.util.stream.Stream<java.lang.String> lines();
method public int read(char[], int, int) throws java.io.IOException;
method public java.lang.String readLine() throws java.io.IOException;
}
@@ -49998,6 +50014,11 @@
ctor public UTFDataFormatException(java.lang.String);
}
+ public class UncheckedIOException extends java.lang.RuntimeException {
+ ctor public UncheckedIOException(java.lang.String, java.io.IOException);
+ ctor public UncheckedIOException(java.io.IOException);
+ }
+
public class UnsupportedEncodingException extends java.io.IOException {
ctor public UnsupportedEncodingException();
ctor public UnsupportedEncodingException(java.lang.String);
@@ -57654,6 +57675,7 @@
method public void set(int, int);
method public void set(int, int, boolean);
method public int size();
+ method public java.util.stream.IntStream stream();
method public byte[] toByteArray();
method public long[] toLongArray();
method public static java.util.BitSet valueOf(long[]);
@@ -58109,6 +58131,7 @@
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
method public boolean replace(K, V, V);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
}
public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
@@ -58152,6 +58175,7 @@
method public synchronized boolean remove(java.lang.Object, java.lang.Object);
method public synchronized boolean replace(K, V, V);
method public synchronized V replace(K, V);
+ method public synchronized void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public synchronized int size();
method public java.util.Collection<V> values();
}
@@ -58692,6 +58716,18 @@
public class Random implements java.io.Serializable {
ctor public Random();
ctor public Random(long);
+ method public java.util.stream.DoubleStream doubles(long);
+ method public java.util.stream.DoubleStream doubles();
+ method public java.util.stream.DoubleStream doubles(long, double, double);
+ method public java.util.stream.DoubleStream doubles(double, double);
+ method public java.util.stream.IntStream ints(long);
+ method public java.util.stream.IntStream ints();
+ method public java.util.stream.IntStream ints(long, int, int);
+ method public java.util.stream.IntStream ints(int, int);
+ method public java.util.stream.LongStream longs(long);
+ method public java.util.stream.LongStream longs();
+ method public java.util.stream.LongStream longs(long, long, long);
+ method public java.util.stream.LongStream longs(long, long);
method protected int next(int);
method public boolean nextBoolean();
method public void nextBytes(byte[]);
@@ -59133,6 +59169,7 @@
method public java.util.NavigableSet<K> navigableKeySet();
method public java.util.Map.Entry<K, V> pollFirstEntry();
method public java.util.Map.Entry<K, V> pollLastEntry();
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public java.util.NavigableMap<K, V> subMap(K, boolean, K, boolean);
method public java.util.SortedMap<K, V> subMap(K, K);
method public java.util.NavigableMap<K, V> tailMap(K, boolean);
@@ -59234,6 +59271,7 @@
ctor public WeakHashMap(java.util.Map<? extends K, ? extends V>);
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
}
}
@@ -60207,18 +60245,6 @@
public class ThreadLocalRandom extends java.util.Random {
method public static java.util.concurrent.ThreadLocalRandom current();
- method public java.util.stream.DoubleStream doubles(long);
- method public java.util.stream.DoubleStream doubles();
- method public java.util.stream.DoubleStream doubles(long, double, double);
- method public java.util.stream.DoubleStream doubles(double, double);
- method public java.util.stream.IntStream ints(long);
- method public java.util.stream.IntStream ints();
- method public java.util.stream.IntStream ints(long, int, int);
- method public java.util.stream.IntStream ints(int, int);
- method public java.util.stream.LongStream longs(long);
- method public java.util.stream.LongStream longs();
- method public java.util.stream.LongStream longs(long, long, long);
- method public java.util.stream.LongStream longs(long, long);
method public double nextDouble(double);
method public double nextDouble(double, double);
method public int nextInt(int, int);
@@ -61591,6 +61617,7 @@
}
public final class Pattern implements java.io.Serializable {
+ method public java.util.function.Predicate<java.lang.String> asPredicate();
method public static java.util.regex.Pattern compile(java.lang.String);
method public static java.util.regex.Pattern compile(java.lang.String, int) throws java.util.regex.PatternSyntaxException;
method public int flags();
@@ -61600,6 +61627,7 @@
method public static java.lang.String quote(java.lang.String);
method public java.lang.String[] split(java.lang.CharSequence, int);
method public java.lang.String[] split(java.lang.CharSequence);
+ method public java.util.stream.Stream<java.lang.String> splitAsStream(java.lang.CharSequence);
field public static final int CANON_EQ = 128; // 0x80
field public static final int CASE_INSENSITIVE = 2; // 0x2
field public static final int COMMENTS = 4; // 0x4
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 672a706..7652766 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1676,6 +1676,17 @@
public void onProvideAssistContent(AssistContent outContent) {
}
+ /**
+ * Request the Keyboard Shortcuts screen to show up. If it succeeds, this will trigger
+ * {@link #onProvideKeyboardShortcuts} to retrieve the shortcuts for the foreground activity.
+ */
+ public final void requestKeyboardShortcutsHelper() {
+ Intent intent = new Intent(Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS);
+ intent.setComponent(new ComponentName("com.android.systemui",
+ "com.android.systemui.statusbar.KeyboardShortcutsReceiver"));
+ sendBroadcast(intent);
+ }
+
@Override
public void onProvideKeyboardShortcuts(
List<KeyboardShortcutGroup> data, Menu menu, int deviceId) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d1f5143..631a129 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Matrix;
@@ -1305,6 +1306,12 @@
*/
public boolean isDockable;
+ /**
+ * The resize mode of the task. See {@link ActivityInfo#resizeMode}.
+ * @hide
+ */
+ public int resizeMode;
+
public RecentTaskInfo() {
}
@@ -1349,6 +1356,7 @@
dest.writeInt(0);
}
dest.writeInt(isDockable ? 1 : 0);
+ dest.writeInt(resizeMode);
}
public void readFromParcel(Parcel source) {
@@ -1372,6 +1380,7 @@
bounds = source.readInt() > 0 ?
Rect.CREATOR.createFromParcel(source) : null;
isDockable = source.readInt() == 1;
+ resizeMode = source.readInt();
}
public static final Creator<RecentTaskInfo> CREATOR
@@ -1560,6 +1569,12 @@
*/
public boolean isDockable;
+ /**
+ * The resize mode of the task. See {@link ActivityInfo#resizeMode}.
+ * @hide
+ */
+ public int resizeMode;
+
public RunningTaskInfo() {
}
@@ -1583,6 +1598,7 @@
dest.writeInt(numActivities);
dest.writeInt(numRunning);
dest.writeInt(isDockable ? 1 : 0);
+ dest.writeInt(resizeMode);
}
public void readFromParcel(Parcel source) {
@@ -1599,6 +1615,7 @@
numActivities = source.readInt();
numRunning = source.readInt();
isDockable = source.readInt() != 0;
+ resizeMode = source.readInt();
}
public static final Creator<RunningTaskInfo> CREATOR = new Creator<RunningTaskInfo>() {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index ed590e6..bf56f25 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1921,6 +1921,16 @@
}
@Override
+ public void deleteApplicationCacheFilesAsUser(String packageName, int userId,
+ IPackageDataObserver observer) {
+ try {
+ mPM.deleteApplicationCacheFilesAsUser(packageName, userId, observer);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
public void freeStorageAndNotify(String volumeUuid, long idealStorageSize,
IPackageDataObserver observer) {
try {
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index f0e35c9..f203f46 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -318,7 +318,7 @@
if (mStarted) {
if (mReportNextStart) {
mReportNextStart = false;
- if (mHaveData) {
+ if (mHaveData && !mRetaining) {
callOnLoadFinished(mLoader, mData);
}
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4bf1aa3..052874f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -29,6 +29,7 @@
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -43,6 +44,7 @@
import android.os.Parcelable;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.text.BidiFormatter;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
@@ -588,8 +590,8 @@
private static final int COLOR_INVALID = 1;
/**
- * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
- * the notification's presence and contents in untrusted situations (namely, on the secure
+ * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
+ * the notification's presence and contents in untrusted situations (namely, on the secure
* lockscreen).
*
* The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
@@ -1419,7 +1421,7 @@
* an activity and transitions should be generated, false otherwise.
* @return this object for method chaining
*/
- public WearableExtender setHintContentIntentLaunchesActivity(
+ public WearableExtender setHintLaunchesActivity(
boolean hintLaunchesActivity) {
setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
return this;
@@ -1432,7 +1434,7 @@
* should be generated, false otherwise. The default value is {@code false} if this was
* never set.
*/
- public boolean getHintContentIntentLaunchesActivity() {
+ public boolean getHintLaunchesActivity() {
return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
}
}
@@ -1955,9 +1957,16 @@
* @hide
*/
public static void addFieldsFromContext(Context context, Notification notification) {
- notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO,
- context.getApplicationInfo());
- notification.extras.putInt(EXTRA_ORIGINATING_USERID, context.getUserId());
+ addFieldsFromContext(context.getApplicationInfo(), context.getUserId(), notification);
+ }
+
+ /**
+ * @hide
+ */
+ public static void addFieldsFromContext(ApplicationInfo ai, int userId,
+ Notification notification) {
+ notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
+ notification.extras.putInt(EXTRA_ORIGINATING_USERID, userId);
}
@Override
@@ -2220,7 +2229,8 @@
Log.d(TAG, "Unknown style class: " + templateClass);
} else {
try {
- final Constructor<? extends Style> ctor = styleClass.getConstructor();
+ final Constructor<? extends Style> ctor =
+ styleClass.getDeclaredConstructor();
ctor.setAccessible(true);
final Style style = ctor.newInstance();
style.restoreFromExtras(mN.extras);
@@ -3119,6 +3129,18 @@
* @param hasProgress whether the progress bar should be shown and set
*/
private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
+ final Bundle ex = mN.extras;
+
+ CharSequence title = processLegacyText(ex.getCharSequence(EXTRA_TITLE));
+ CharSequence text = processLegacyText(ex.getCharSequence(EXTRA_TEXT));
+ return applyStandardTemplate(resId, hasProgress, title, text);
+ }
+
+ /**
+ * @param hasProgress whether the progress bar should be shown and set
+ */
+ private RemoteViews applyStandardTemplate(int resId, boolean hasProgress,
+ CharSequence title, CharSequence text) {
RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
resetStandardTemplate(contentView);
@@ -3127,17 +3149,15 @@
bindNotificationHeader(contentView);
bindLargeIcon(contentView);
- if (ex.getCharSequence(EXTRA_TITLE) != null) {
+ if (title != null) {
contentView.setViewVisibility(R.id.title, View.VISIBLE);
- contentView.setTextViewText(R.id.title,
- processLegacyText(ex.getCharSequence(EXTRA_TITLE)));
+ contentView.setTextViewText(R.id.title, title);
}
boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
- if (ex.getCharSequence(EXTRA_TEXT) != null) {
+ if (text != null) {
int textId = showProgress ? com.android.internal.R.id.text_line_1
: com.android.internal.R.id.text;
- contentView.setTextViewText(textId, processLegacyText(
- ex.getCharSequence(EXTRA_TEXT)));
+ contentView.setTextViewText(textId, text);
contentView.setViewVisibility(textId, View.VISIBLE);
}
@@ -3288,7 +3308,16 @@
}
private RemoteViews applyStandardTemplateWithActions(int layoutId) {
- RemoteViews big = applyStandardTemplate(layoutId);
+ final Bundle ex = mN.extras;
+
+ CharSequence title = processLegacyText(ex.getCharSequence(EXTRA_TITLE));
+ CharSequence text = processLegacyText(ex.getCharSequence(EXTRA_TEXT));
+ return applyStandardTemplateWithActions(layoutId, true /* hasProgress */, title, text);
+ }
+
+ private RemoteViews applyStandardTemplateWithActions(int layoutId, boolean hasProgress,
+ CharSequence title, CharSequence text) {
+ RemoteViews big = applyStandardTemplate(layoutId, hasProgress, title, text);
resetStandardTemplateWithActions(big);
@@ -3742,6 +3771,10 @@
return R.layout.notification_template_material_inbox;
}
+ private int getMessagingLayoutResource() {
+ return R.layout.notification_template_material_messaging;
+ }
+
private int getActionLayoutResource() {
return R.layout.notification_material_action;
}
@@ -4368,13 +4401,100 @@
/**
* @hide
*/
- public RemoteViews makeBigContentView() {
- // TODO handset to write implementation
- RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
+ @Override
+ public RemoteViews makeContentView() {
+ Message m = findLatestIncomingMessage();
+ CharSequence title = mConversationTitle != null
+ ? mConversationTitle
+ : (m == null) ? null : m.mSender;
+ CharSequence text = (m == null)
+ ? null
+ : mConversationTitle != null ? makeMessageLine(m) : m.mText;
+ return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
+ false /* hasProgress */,
+ title,
+ text);
+ }
+
+ private Message findLatestIncomingMessage() {
+ for (int i = mMessages.size() - 1; i >= 0; i--) {
+ Message m = mMessages.get(i);
+ // Incoming messages have a non-empty sender.
+ if (!TextUtils.isEmpty(m.mSender)) {
+ return m;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public RemoteViews makeBigContentView() {
+ CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
+ ? super.mBigContentTitle
+ : mConversationTitle;
+ boolean hasTitle = !TextUtils.isEmpty(title);
+
+ RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
+ mBuilder.getMessagingLayoutResource(),
+ false /* hasProgress */,
+ title,
+ null /* text */);
+
+ int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
+ R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
+
+ // Make sure all rows are gone in case we reuse a view.
+ for (int rowId : rowIds) {
+ contentView.setViewVisibility(rowId, View.GONE);
+ }
+
+ int i=0;
+ int titlePadding = mBuilder.mContext.getResources().getDimensionPixelSize(
+ R.dimen.notification_messaging_spacing);
+ contentView.setViewLayoutMarginBottom(R.id.line1, hasTitle ? titlePadding : 0);
+ contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
+ mBuilder.mN.mLargeIcon == null ? 0 : (hasTitle ? 1 : 2));
+
+ int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
+ while (firstMessage + i < mMessages.size() && i < rowIds.length) {
+ Message m = mMessages.get(firstMessage + i);
+ int rowId = rowIds[i];
+
+ contentView.setViewVisibility(rowId, View.VISIBLE);
+ contentView.setTextViewText(rowId, makeMessageLine(m));
+
+ i++;
+ }
return contentView;
}
+ private CharSequence makeMessageLine(Message m) {
+ BidiFormatter bidi = BidiFormatter.getInstance();
+ SpannableStringBuilder sb = new SpannableStringBuilder();
+ if (TextUtils.isEmpty(m.mSender)) {
+ CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
+ sb.append(bidi.unicodeWrap(replyName),
+ makeFontColorSpan(mBuilder.resolveContrastColor()),
+ 0 /* flags */);
+ } else {
+ sb.append(bidi.unicodeWrap(m.mSender),
+ makeFontColorSpan(Color.BLACK),
+ 0 /* flags */);
+ }
+ CharSequence text = m.mText == null ? "" : m.mText;
+ sb.append(" ").append(bidi.unicodeWrap(text));
+ return sb;
+ }
+
+ private static TextAppearanceSpan makeFontColorSpan(int color) {
+ return new TextAppearanceSpan(null, 0, 0,
+ ColorStateList.valueOf(color), null);
+ }
+
public static final class Message implements Parcelable {
private final CharSequence mText;
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index bdc4404..d5d4ca7 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -589,9 +589,7 @@
new CachedServiceFetcher<LauncherApps>() {
@Override
public LauncherApps createService(ContextImpl ctx) {
- IBinder b = ServiceManager.getService(Context.LAUNCHER_APPS_SERVICE);
- ILauncherApps service = ILauncherApps.Stub.asInterface(b);
- return new LauncherApps(ctx, service);
+ return new LauncherApps(ctx);
}});
registerService(Context.RESTRICTIONS_SERVICE, RestrictionsManager.class,
@@ -758,8 +756,7 @@
new CachedServiceFetcher<ShortcutManager>() {
@Override
public ShortcutManager createService(ContextImpl ctx) {
- IBinder b = ServiceManager.getService(Context.SHORTCUT_SERVICE);
- return new ShortcutManager(ctx, IShortcutService.Stub.asInterface(b));
+ return new ShortcutManager(ctx);
}});
registerService(Context.SYSTEM_HEALTH_SERVICE, SystemHealthManager.class,
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3f9629c8..96757bb 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2216,7 +2216,9 @@
* that uses {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD}
*/
public boolean resetPassword(String password, int flags) {
- throwIfParentInstance("resetPassword");
+ if (mParentInstance) {
+ throw new SecurityException("Reset password does not work across profiles.");
+ }
if (mService != null) {
try {
return mService.resetPassword(password, flags);
@@ -2353,7 +2355,6 @@
* that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
*/
public void wipeData(int flags) {
- throwIfParentInstance("wipeData");
if (mService != null) {
try {
mService.wipeData(flags);
@@ -2387,7 +2388,6 @@
*/
public ComponentName setGlobalProxy(@NonNull ComponentName admin, Proxy proxySpec,
List<String> exclusionList ) {
- throwIfParentInstance("setGlobalProxy");
if (proxySpec == null) {
throw new NullPointerException();
}
@@ -2453,7 +2453,6 @@
*/
public void setRecommendedGlobalProxy(@NonNull ComponentName admin, @Nullable ProxyInfo
proxyInfo) {
- throwIfParentInstance("setRecommendedGlobalProxy");
if (mService != null) {
try {
mService.setRecommendedGlobalProxy(admin, proxyInfo);
@@ -2604,7 +2603,6 @@
* {@link DeviceAdminInfo#USES_ENCRYPTED_STORAGE}
*/
public int setStorageEncryption(@NonNull ComponentName admin, boolean encrypt) {
- throwIfParentInstance("setStorageEncryption");
if (mService != null) {
try {
return mService.setStorageEncryption(admin, encrypt);
@@ -2625,7 +2623,6 @@
* @return true if the admin(s) are requesting encryption, false if not.
*/
public boolean getStorageEncryption(@Nullable ComponentName admin) {
- throwIfParentInstance("getStorageEncryption");
if (mService != null) {
try {
return mService.getStorageEncryption(admin, myUserId());
@@ -2721,7 +2718,6 @@
* owner.
*/
public boolean installCaCert(@Nullable ComponentName admin, byte[] certBuffer) {
- throwIfParentInstance("installCaCert");
if (mService != null) {
try {
return mService.installCaCert(admin, certBuffer);
@@ -2742,7 +2738,6 @@
* owner.
*/
public void uninstallCaCert(@Nullable ComponentName admin, byte[] certBuffer) {
- throwIfParentInstance("uninstallCaCert");
if (mService != null) {
try {
final String alias = getCaCertAlias(certBuffer);
@@ -2768,7 +2763,6 @@
*/
public List<byte[]> getInstalledCaCerts(@Nullable ComponentName admin) {
List<byte[]> certs = new ArrayList<byte[]>();
- throwIfParentInstance("getInstalledCaCerts");
if (mService != null) {
try {
mService.enforceCanManageCaCerts(admin);
@@ -2797,7 +2791,6 @@
* owner.
*/
public void uninstallAllUserCaCerts(@Nullable ComponentName admin) {
- throwIfParentInstance("uninstallAllUserCaCerts");
if (mService != null) {
try {
mService.uninstallCaCerts(admin, new TrustedCertificateStore().userAliases()
@@ -2818,7 +2811,6 @@
* owner.
*/
public boolean hasCaCertInstalled(@Nullable ComponentName admin, byte[] certBuffer) {
- throwIfParentInstance("hasCaCertInstalled");
if (mService != null) {
try {
mService.enforceCanManageCaCerts(admin);
@@ -2887,7 +2879,6 @@
*/
public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
@NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess) {
- throwIfParentInstance("installKeyPair");
try {
final byte[] pemCert = Credentials.convertToPem(certs[0]);
byte[] pemChain = null;
@@ -2920,7 +2911,6 @@
* owner.
*/
public boolean removeKeyPair(@Nullable ComponentName admin, @NonNull String alias) {
- throwIfParentInstance("removeKeyPair");
try {
return mService.removeKeyPair(admin, alias);
} catch (RemoteException e) {
@@ -2961,7 +2951,6 @@
*/
public void setCertInstallerPackage(@NonNull ComponentName admin, @Nullable String
installerPackage) throws SecurityException {
- throwIfParentInstance("setCertInstallerPackage");
if (mService != null) {
try {
mService.setCertInstallerPackage(admin, installerPackage);
@@ -2981,7 +2970,6 @@
* @throws SecurityException if {@code admin} is not a device or a profile owner.
*/
public String getCertInstallerPackage(@NonNull ComponentName admin) throws SecurityException {
- throwIfParentInstance("getCertInstallerPackage");
if (mService != null) {
try {
return mService.getCertInstallerPackage(admin);
@@ -3012,7 +3000,6 @@
*/
public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage)
throws NameNotFoundException, UnsupportedOperationException {
- throwIfParentInstance("setAlwaysOnVpnPackage");
if (mService != null) {
try {
if (!mService.setAlwaysOnVpnPackage(admin, vpnPackage)) {
@@ -3034,7 +3021,6 @@
* @throws SecurityException if {@code admin} is not a device or a profile owner.
*/
public String getAlwaysOnVpnPackage(@NonNull ComponentName admin) {
- throwIfParentInstance("getAlwaysOnVpnPackage");
if (mService != null) {
try {
return mService.getAlwaysOnVpnPackage(admin);
@@ -3062,7 +3048,6 @@
* {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA}.
*/
public void setCameraDisabled(@NonNull ComponentName admin, boolean disabled) {
- throwIfParentInstance("setCameraDisabled");
if (mService != null) {
try {
mService.setCameraDisabled(admin, disabled);
@@ -3079,7 +3064,6 @@
* have disabled the camera
*/
public boolean getCameraDisabled(@Nullable ComponentName admin) {
- throwIfParentInstance("getCameraDisabled");
return getCameraDisabled(admin, myUserId());
}
@@ -3109,7 +3093,6 @@
* than the one managed by the device owner.
*/
public boolean requestBugreport(@NonNull ComponentName admin) {
- throwIfParentInstance("requestBugreport");
if (mService != null) {
try {
return mService.requestBugreport(admin);
@@ -3148,7 +3131,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public void setScreenCaptureDisabled(@NonNull ComponentName admin, boolean disabled) {
- throwIfParentInstance("setScreenCaptureDisabled");
if (mService != null) {
try {
mService.setScreenCaptureDisabled(admin, disabled);
@@ -3165,7 +3147,6 @@
* have disabled screen capture.
*/
public boolean getScreenCaptureDisabled(@Nullable ComponentName admin) {
- throwIfParentInstance("getScreenCaptureDisabled");
return getScreenCaptureDisabled(admin, myUserId());
}
@@ -3195,7 +3176,6 @@
* @throws SecurityException if {@code admin} is not a device owner.
*/
public void setAutoTimeRequired(@NonNull ComponentName admin, boolean required) {
- throwIfParentInstance("setAutoTimeRequired");
if (mService != null) {
try {
mService.setAutoTimeRequired(admin, required);
@@ -3209,7 +3189,6 @@
* @return true if auto time is required.
*/
public boolean getAutoTimeRequired() {
- throwIfParentInstance("getAutoTimeRequired");
if (mService != null) {
try {
return mService.getAutoTimeRequired();
@@ -3236,7 +3215,6 @@
*/
public void setForceEphemeralUsers(
@NonNull ComponentName admin, boolean forceEphemeralUsers) {
- throwIfParentInstance("setForceEphemeralUsers");
if (mService != null) {
try {
mService.setForceEphemeralUsers(admin, forceEphemeralUsers);
@@ -3252,7 +3230,6 @@
* @hide
*/
public boolean getForceEphemeralUsers(@NonNull ComponentName admin) {
- throwIfParentInstance("getForceEphemeralUsers");
if (mService != null) {
try {
return mService.getForceEphemeralUsers(admin);
@@ -3540,7 +3517,6 @@
* @return whether or not the package is registered as the device owner app.
*/
public boolean isDeviceOwnerApp(String packageName) {
- throwIfParentInstance("isDeviceOwnerApp");
return isDeviceOwnerAppOnCallingUser(packageName);
}
@@ -3638,7 +3614,6 @@
* does not own the current device owner component.
*/
public void clearDeviceOwnerApp(String packageName) {
- throwIfParentInstance("clearDeviceOwnerApp");
if (mService != null) {
try {
mService.clearDeviceOwner(packageName);
@@ -3756,7 +3731,6 @@
* @throws SecurityException if {@code admin} is not an active profile owner.
*/
public void clearProfileOwner(@NonNull ComponentName admin) {
- throwIfParentInstance("clearProfileOwner");
if (mService != null) {
try {
mService.clearProfileOwner(admin);
@@ -3830,7 +3804,6 @@
* @throws SecurityException if {@code admin} is not a device owner.
*/
public void setDeviceOwnerLockScreenInfo(@NonNull ComponentName admin, CharSequence info) {
- throwIfParentInstance("setDeviceOwnerLockScreenInfo");
if (mService != null) {
try {
mService.setDeviceOwnerLockScreenInfo(admin, info);
@@ -3844,7 +3817,6 @@
* @return The device owner information. If it is not set returns {@code null}.
*/
public CharSequence getDeviceOwnerLockScreenInfo() {
- throwIfParentInstance("getDeviceOwnerLockScreenInfo");
if (mService != null) {
try {
return mService.getDeviceOwnerLockScreenInfo();
@@ -3876,7 +3848,6 @@
*/
public String[] setPackagesSuspended(@NonNull ComponentName admin, String[] packageNames,
boolean suspended) {
- throwIfParentInstance("setPackagesSuspended");
if (mService != null) {
try {
return mService.setPackagesSuspended(admin, packageNames, suspended);
@@ -3899,7 +3870,6 @@
*/
public boolean isPackageSuspended(@NonNull ComponentName admin, String packageName)
throws NameNotFoundException {
- throwIfParentInstance("isPackageSuspended");
if (mService != null) {
try {
return mService.isPackageSuspended(admin, packageName);
@@ -3921,7 +3891,6 @@
* @throws SecurityException if {@code admin} is not a profile owner.
*/
public void setProfileEnabled(@NonNull ComponentName admin) {
- throwIfParentInstance("setProfileEnabled");
if (mService != null) {
try {
mService.setProfileEnabled(admin);
@@ -3943,7 +3912,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public void setProfileName(@NonNull ComponentName admin, String profileName) {
- throwIfParentInstance("setProfileName");
if (mService != null) {
try {
mService.setProfileName(admin, profileName);
@@ -3962,7 +3930,6 @@
* @return Whether or not the package is registered as the profile owner.
*/
public boolean isProfileOwnerApp(String packageName) {
- throwIfParentInstance("isProfileOwnerApp");
if (mService != null) {
try {
ComponentName profileOwner = mService.getProfileOwner(myUserId());
@@ -4057,7 +4024,6 @@
*/
public void addPersistentPreferredActivity(@NonNull ComponentName admin, IntentFilter filter,
@NonNull ComponentName activity) {
- throwIfParentInstance("addPersistentPreferredActivity");
if (mService != null) {
try {
mService.addPersistentPreferredActivity(admin, filter, activity);
@@ -4080,7 +4046,6 @@
*/
public void clearPackagePersistentPreferredActivities(@NonNull ComponentName admin,
String packageName) {
- throwIfParentInstance("clearPackagePersistentPreferredActivities");
if (mService != null) {
try {
mService.clearPackagePersistentPreferredActivities(admin, packageName);
@@ -4109,7 +4074,6 @@
*/
public void setApplicationRestrictionsManagingPackage(@NonNull ComponentName admin,
@Nullable String packageName) throws NameNotFoundException {
- throwIfParentInstance("setApplicationRestrictionsManagingPackage");
if (mService != null) {
try {
if (!mService.setApplicationRestrictionsManagingPackage(admin, packageName)) {
@@ -4131,7 +4095,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public String getApplicationRestrictionsManagingPackage(@NonNull ComponentName admin) {
- throwIfParentInstance("getApplicationRestrictionsManagingPackage");
if (mService != null) {
try {
return mService.getApplicationRestrictionsManagingPackage(admin);
@@ -4151,7 +4114,6 @@
* that method.
*/
public boolean isCallerApplicationRestrictionsManagingPackage() {
- throwIfParentInstance("isCallerApplicationRestrictionsManagingPackage");
if (mService != null) {
try {
return mService.isCallerApplicationRestrictionsManagingPackage();
@@ -4197,7 +4159,6 @@
*/
public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName,
Bundle settings) {
- throwIfParentInstance("setApplicationRestrictions");
if (mService != null) {
try {
mService.setApplicationRestrictions(admin, packageName, settings);
@@ -4296,7 +4257,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public void setCrossProfileCallerIdDisabled(@NonNull ComponentName admin, boolean disabled) {
- throwIfParentInstance("setCrossProfileCallerIdDisabled");
if (mService != null) {
try {
mService.setCrossProfileCallerIdDisabled(admin, disabled);
@@ -4317,7 +4277,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public boolean getCrossProfileCallerIdDisabled(@NonNull ComponentName admin) {
- throwIfParentInstance("getCrossProfileCallerIdDisabled");
if (mService != null) {
try {
return mService.getCrossProfileCallerIdDisabled(admin);
@@ -4358,7 +4317,6 @@
*/
public void setCrossProfileContactsSearchDisabled(@NonNull ComponentName admin,
boolean disabled) {
- throwIfParentInstance("setCrossProfileContactsSearchDisabled");
if (mService != null) {
try {
mService.setCrossProfileContactsSearchDisabled(admin, disabled);
@@ -4379,7 +4337,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public boolean getCrossProfileContactsSearchDisabled(@NonNull ComponentName admin) {
- throwIfParentInstance("getCrossProfileContactsSearchDisabled");
if (mService != null) {
try {
return mService.getCrossProfileContactsSearchDisabled(admin);
@@ -4450,7 +4407,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public void setBluetoothContactSharingDisabled(@NonNull ComponentName admin, boolean disabled) {
- throwIfParentInstance("setBluetoothContactSharingDisabled");
if (mService != null) {
try {
mService.setBluetoothContactSharingDisabled(admin, disabled);
@@ -4473,7 +4429,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public boolean getBluetoothContactSharingDisabled(@NonNull ComponentName admin) {
- throwIfParentInstance("getBluetoothContactSharingDisabled");
if (mService != null) {
try {
return mService.getBluetoothContactSharingDisabled(admin);
@@ -4517,7 +4472,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public void addCrossProfileIntentFilter(@NonNull ComponentName admin, IntentFilter filter, int flags) {
- throwIfParentInstance("addCrossProfileIntentFilter");
if (mService != null) {
try {
mService.addCrossProfileIntentFilter(admin, filter, flags);
@@ -4536,7 +4490,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public void clearCrossProfileIntentFilters(@NonNull ComponentName admin) {
- throwIfParentInstance("clearCrossProfileIntentFilters");
if (mService != null) {
try {
mService.clearCrossProfileIntentFilters(admin);
@@ -4566,7 +4519,6 @@
*/
public boolean setPermittedAccessibilityServices(@NonNull ComponentName admin,
List<String> packageNames) {
- throwIfParentInstance("setPermittedAccessibilityServices");
if (mService != null) {
try {
return mService.setPermittedAccessibilityServices(admin, packageNames);
@@ -4588,7 +4540,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public List<String> getPermittedAccessibilityServices(@NonNull ComponentName admin) {
- throwIfParentInstance("getPermittedAccessibilityServices");
if (mService != null) {
try {
return mService.getPermittedAccessibilityServices(admin);
@@ -4666,7 +4617,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public boolean setPermittedInputMethods(@NonNull ComponentName admin, List<String> packageNames) {
- throwIfParentInstance("setPermittedInputMethods");
if (mService != null) {
try {
return mService.setPermittedInputMethods(admin, packageNames);
@@ -4689,7 +4639,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public List<String> getPermittedInputMethods(@NonNull ComponentName admin) {
- throwIfParentInstance("getPermittedInputMethods");
if (mService != null) {
try {
return mService.getPermittedInputMethods(admin);
@@ -4885,7 +4834,6 @@
public UserHandle createAndManageUser(@NonNull ComponentName admin, @NonNull String name,
@NonNull ComponentName profileOwner, @Nullable PersistableBundle adminExtras,
int flags) {
- throwIfParentInstance("createAndManageUser");
try {
return mService.createAndManageUser(admin, name, profileOwner, adminExtras, flags);
} catch (RemoteException re) {
@@ -4903,7 +4851,6 @@
* @throws SecurityException if {@code admin} is not a device owner.
*/
public boolean removeUser(@NonNull ComponentName admin, UserHandle userHandle) {
- throwIfParentInstance("removeUser");
try {
return mService.removeUser(admin, userHandle);
} catch (RemoteException re) {
@@ -4921,7 +4868,6 @@
* @see Intent#ACTION_USER_FOREGROUND
*/
public boolean switchUser(@NonNull ComponentName admin, @Nullable UserHandle userHandle) {
- throwIfParentInstance("switchUser");
try {
return mService.switchUser(admin, userHandle);
} catch (RemoteException re) {
@@ -4947,7 +4893,6 @@
* @see {@link #setApplicationRestrictionsManagingPackage}
*/
public Bundle getApplicationRestrictions(@Nullable ComponentName admin, String packageName) {
- throwIfParentInstance("getApplicationRestrictions");
if (mService != null) {
try {
return mService.getApplicationRestrictions(admin, packageName);
@@ -4970,7 +4915,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public void addUserRestriction(@NonNull ComponentName admin, String key) {
- throwIfParentInstance("addUserRestriction");
if (mService != null) {
try {
mService.setUserRestriction(admin, key, true);
@@ -4992,7 +4936,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public void clearUserRestriction(@NonNull ComponentName admin, String key) {
- throwIfParentInstance("clearUserRestriction");
if (mService != null) {
try {
mService.setUserRestriction(admin, key, false);
@@ -5014,7 +4957,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public Bundle getUserRestrictions(@NonNull ComponentName admin) {
- throwIfParentInstance("getUserRestrictions");
Bundle ret = null;
if (mService != null) {
try {
@@ -5059,7 +5001,6 @@
*/
public boolean setApplicationHidden(@NonNull ComponentName admin, String packageName,
boolean hidden) {
- throwIfParentInstance("setApplicationHidden");
if (mService != null) {
try {
return mService.setApplicationHidden(admin, packageName, hidden);
@@ -5079,7 +5020,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public boolean isApplicationHidden(@NonNull ComponentName admin, String packageName) {
- throwIfParentInstance("isApplicationHidden");
if (mService != null) {
try {
return mService.isApplicationHidden(admin, packageName);
@@ -5099,7 +5039,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public void enableSystemApp(@NonNull ComponentName admin, String packageName) {
- throwIfParentInstance("enableSystemApp");
if (mService != null) {
try {
mService.enableSystemApp(admin, packageName);
@@ -5120,7 +5059,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public int enableSystemApp(@NonNull ComponentName admin, Intent intent) {
- throwIfParentInstance("enableSystemApp");
if (mService != null) {
try {
return mService.enableSystemAppWithIntent(admin, intent);
@@ -5153,7 +5091,6 @@
*/
public void setAccountManagementDisabled(@NonNull ComponentName admin, String accountType,
boolean disabled) {
- throwIfParentInstance("setAccountManagementDisabled");
if (mService != null) {
try {
mService.setAccountManagementDisabled(admin, accountType, disabled);
@@ -5174,7 +5111,6 @@
* @see #setAccountManagementDisabled
*/
public String[] getAccountTypesWithManagementDisabled() {
- throwIfParentInstance("getAccountTypesWithManagementDisabled");
return getAccountTypesWithManagementDisabledAsUser(myUserId());
}
@@ -5212,7 +5148,6 @@
*/
public void setLockTaskPackages(@NonNull ComponentName admin, String[] packages)
throws SecurityException {
- throwIfParentInstance("setLockTaskPackages");
if (mService != null) {
try {
mService.setLockTaskPackages(admin, packages);
@@ -5229,7 +5164,6 @@
* @hide
*/
public String[] getLockTaskPackages(@NonNull ComponentName admin) {
- throwIfParentInstance("getLockTaskPackages");
if (mService != null) {
try {
return mService.getLockTaskPackages(admin);
@@ -5246,7 +5180,6 @@
* @param pkg The package to check
*/
public boolean isLockTaskPermitted(String pkg) {
- throwIfParentInstance("isLockTaskPermitted");
if (mService != null) {
try {
return mService.isLockTaskPermitted(pkg);
@@ -5295,7 +5228,6 @@
* @throws SecurityException if {@code admin} is not a device owner.
*/
public void setGlobalSetting(@NonNull ComponentName admin, String setting, String value) {
- throwIfParentInstance("setGlobalSetting");
if (mService != null) {
try {
mService.setGlobalSetting(admin, setting, value);
@@ -5328,7 +5260,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public void setSecureSetting(@NonNull ComponentName admin, String setting, String value) {
- throwIfParentInstance("setSecureSetting");
if (mService != null) {
try {
mService.setSecureSetting(admin, setting, value);
@@ -5352,7 +5283,6 @@
*/
public void setRestrictionsProvider(@NonNull ComponentName admin,
@Nullable ComponentName provider) {
- throwIfParentInstance("setRestrictionsProvider");
if (mService != null) {
try {
mService.setRestrictionsProvider(admin, provider);
@@ -5370,7 +5300,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public void setMasterVolumeMuted(@NonNull ComponentName admin, boolean on) {
- throwIfParentInstance("setMasterVolumeMuted");
if (mService != null) {
try {
mService.setMasterVolumeMuted(admin, on);
@@ -5388,7 +5317,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public boolean isMasterVolumeMuted(@NonNull ComponentName admin) {
- throwIfParentInstance("isMasterVolumeMuted");
if (mService != null) {
try {
return mService.isMasterVolumeMuted(admin);
@@ -5409,7 +5337,6 @@
*/
public void setUninstallBlocked(@NonNull ComponentName admin, String packageName,
boolean uninstallBlocked) {
- throwIfParentInstance("setUninstallBlocked");
if (mService != null) {
try {
mService.setUninstallBlocked(admin, packageName, uninstallBlocked);
@@ -5435,7 +5362,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public boolean isUninstallBlocked(@Nullable ComponentName admin, String packageName) {
- throwIfParentInstance("isUninstallBlocked");
if (mService != null) {
try {
return mService.isUninstallBlocked(admin, packageName);
@@ -5463,7 +5389,6 @@
* @see #getCrossProfileWidgetProviders(android.content.ComponentName)
*/
public boolean addCrossProfileWidgetProvider(@NonNull ComponentName admin, String packageName) {
- throwIfParentInstance("addCrossProfileWidgetProvider");
if (mService != null) {
try {
return mService.addCrossProfileWidgetProvider(admin, packageName);
@@ -5491,7 +5416,6 @@
*/
public boolean removeCrossProfileWidgetProvider(
@NonNull ComponentName admin, String packageName) {
- throwIfParentInstance("removeCrossProfileWidgetProvider");
if (mService != null) {
try {
return mService.removeCrossProfileWidgetProvider(admin, packageName);
@@ -5513,7 +5437,6 @@
* @throws SecurityException if {@code admin} is not a profile owner.
*/
public List<String> getCrossProfileWidgetProviders(@NonNull ComponentName admin) {
- throwIfParentInstance("getCrossProfileWidgetProviders");
if (mService != null) {
try {
List<String> providers = mService.getCrossProfileWidgetProviders(admin);
@@ -5535,7 +5458,6 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public void setUserIcon(@NonNull ComponentName admin, Bitmap icon) {
- throwIfParentInstance("setUserIcon");
try {
mService.setUserIcon(admin, icon);
} catch (RemoteException re) {
@@ -5555,7 +5477,6 @@
* @see SystemUpdatePolicy
*/
public void setSystemUpdatePolicy(@NonNull ComponentName admin, SystemUpdatePolicy policy) {
- throwIfParentInstance("setSystemUpdatePolicy");
if (mService != null) {
try {
mService.setSystemUpdatePolicy(admin, policy);
@@ -5571,7 +5492,6 @@
* @return The current policy object, or {@code null} if no policy is set.
*/
public SystemUpdatePolicy getSystemUpdatePolicy() {
- throwIfParentInstance("getSystemUpdatePolicy");
if (mService != null) {
try {
return mService.getSystemUpdatePolicy();
@@ -5597,7 +5517,6 @@
* @throws SecurityException if {@code admin} is not a device owner.
*/
public boolean setKeyguardDisabled(@NonNull ComponentName admin, boolean disabled) {
- throwIfParentInstance("setKeyguardDisabled");
try {
return mService.setKeyguardDisabled(admin, disabled);
} catch (RemoteException re) {
@@ -5616,7 +5535,6 @@
* @throws SecurityException if {@code admin} is not a device owner.
*/
public boolean setStatusBarDisabled(@NonNull ComponentName admin, boolean disabled) {
- throwIfParentInstance("setStatusBarDisabled");
try {
return mService.setStatusBarDisabled(admin, disabled);
} catch (RemoteException re) {
@@ -5662,7 +5580,6 @@
* @see #setPermissionGrantState
*/
public void setPermissionPolicy(@NonNull ComponentName admin, int policy) {
- throwIfParentInstance("setPermissionPolicy");
try {
mService.setPermissionPolicy(admin, policy);
} catch (RemoteException re) {
@@ -5677,7 +5594,6 @@
* @return the current policy for future permission requests.
*/
public int getPermissionPolicy(ComponentName admin) {
- throwIfParentInstance("getPermissionPolicy");
try {
return mService.getPermissionPolicy(admin);
} catch (RemoteException re) {
@@ -5714,7 +5630,6 @@
*/
public boolean setPermissionGrantState(@NonNull ComponentName admin, String packageName,
String permission, int grantState) {
- throwIfParentInstance("setPermissionGrantState");
try {
return mService.setPermissionGrantState(admin, packageName, permission, grantState);
} catch (RemoteException re) {
@@ -5743,7 +5658,6 @@
*/
public int getPermissionGrantState(@NonNull ComponentName admin, String packageName,
String permission) {
- throwIfParentInstance("getPermissionGrantState");
try {
return mService.getPermissionGrantState(admin, packageName, permission);
} catch (RemoteException re) {
@@ -5759,7 +5673,6 @@
* @throws IllegalArgumentException if the supplied action is not valid.
*/
public boolean isProvisioningAllowed(String action) {
- throwIfParentInstance("isProvisioningAllowed");
try {
return mService.isProvisioningAllowed(action);
} catch (RemoteException re) {
@@ -5775,7 +5688,6 @@
* @return if this user is a managed profile of another user.
*/
public boolean isManagedProfile(@NonNull ComponentName admin) {
- throwIfParentInstance("isManagedProfile");
try {
return mService.isManagedProfile(admin);
} catch (RemoteException re) {
@@ -5809,7 +5721,6 @@
* @throws SecurityException if {@code admin} is not a device owner.
*/
public String getWifiMacAddress(@NonNull ComponentName admin) {
- throwIfParentInstance("getWifiMacAddress");
try {
return mService.getWifiMacAddress(admin);
} catch (RemoteException re) {
@@ -5826,7 +5737,6 @@
* @see TelephonyManager#CALL_STATE_IDLE
*/
public void reboot(@NonNull ComponentName admin) {
- throwIfParentInstance("reboot");
try {
mService.reboot(admin);
} catch (RemoteException re) {
@@ -5853,7 +5763,6 @@
*/
public void setShortSupportMessage(@NonNull ComponentName admin,
@Nullable String message) {
- throwIfParentInstance("setShortSupportMessage");
if (mService != null) {
try {
mService.setShortSupportMessage(admin, message);
@@ -5872,7 +5781,6 @@
* @throws SecurityException if {@code admin} is not an active administrator.
*/
public String getShortSupportMessage(@NonNull ComponentName admin) {
- throwIfParentInstance("getShortSupportMessage");
if (mService != null) {
try {
return mService.getShortSupportMessage(admin);
@@ -5899,7 +5807,6 @@
*/
public void setLongSupportMessage(@NonNull ComponentName admin,
@Nullable String message) {
- throwIfParentInstance("setLongSupportMessage");
if (mService != null) {
try {
mService.setLongSupportMessage(admin, message);
@@ -5918,7 +5825,6 @@
* @throws SecurityException if {@code admin} is not an active administrator.
*/
public String getLongSupportMessage(@NonNull ComponentName admin) {
- throwIfParentInstance("getLongSupportMessage");
if (mService != null) {
try {
return mService.getLongSupportMessage(admin);
@@ -6015,7 +5921,6 @@
* @throws SecurityException if {@code admin} is not a profile owner.
*/
public DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
- throwIfParentInstance("getParentProfileInstance");
try {
if (!mService.isManagedProfile(admin)) {
throw new SecurityException("The current user does not have a parent profile.");
@@ -6042,7 +5947,6 @@
* @see #retrieveSecurityLogs
*/
public void setSecurityLoggingEnabled(@NonNull ComponentName admin, boolean enabled) {
- throwIfParentInstance("setSecurityLoggingEnabled");
try {
mService.setSecurityLoggingEnabled(admin, enabled);
} catch (RemoteException re) {
@@ -6061,7 +5965,6 @@
* @throws SecurityException if {@code admin} is not a device owner.
*/
public boolean isSecurityLoggingEnabled(@NonNull ComponentName admin) {
- throwIfParentInstance("isSecurityLoggingEnabled");
try {
return mService.isSecurityLoggingEnabled(admin);
} catch (RemoteException re) {
@@ -6085,7 +5988,6 @@
* @throws SecurityException if {@code admin} is not a device owner.
*/
public List<SecurityEvent> retrieveSecurityLogs(@NonNull ComponentName admin) {
- throwIfParentInstance("retrieveSecurityLogs");
try {
ParceledListSlice<SecurityEvent> list = mService.retrieveSecurityLogs(admin);
if (list != null) {
@@ -6131,7 +6033,6 @@
* @throws SecurityException if {@code admin} is not a device owner.
*/
public List<SecurityEvent> retrievePreRebootSecurityLogs(@NonNull ComponentName admin) {
- throwIfParentInstance("retrievePreRebootSecurityLogs");
try {
ParceledListSlice<SecurityEvent> list = mService.retrievePreRebootSecurityLogs(admin);
return list.getList();
@@ -6153,7 +6054,6 @@
* @throws SecurityException if {@code admin} is not a profile owner.
*/
public void setOrganizationColor(@NonNull ComponentName admin, int color) {
- throwIfParentInstance("setOrganizationColor");
try {
mService.setOrganizationColor(admin, color);
} catch (RemoteException re) {
@@ -6189,7 +6089,6 @@
* @throws SecurityException if {@code admin} is not a profile owner.
*/
public int getOrganizationColor(@NonNull ComponentName admin) {
- throwIfParentInstance("getOrganizationColor");
try {
return mService.getOrganizationColor(admin);
} catch (RemoteException re) {
@@ -6225,7 +6124,6 @@
* @throws SecurityException if {@code admin} is not a profile owner.
*/
public void setOrganizationName(@NonNull ComponentName admin, @Nullable String title) {
- throwIfParentInstance("setOrganizationName");
try {
mService.setOrganizationName(admin, title);
} catch (RemoteException re) {
@@ -6242,7 +6140,6 @@
* @throws SecurityException if {@code admin} is not a profile owner.
*/
public String getOrganizationName(@NonNull ComponentName admin) {
- throwIfParentInstance("getOrganizationName");
try {
return mService.getOrganizationName(admin);
} catch (RemoteException re) {
@@ -6368,10 +6265,4 @@
throw re.rethrowFromSystemServer();
}
}
-
- private void throwIfParentInstance(String functionName) {
- if (mParentInstance) {
- throw new SecurityException(functionName + " cannot be called on the parent instance");
- }
- }
}
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 828ac38..9b4f43a 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -254,7 +254,8 @@
}
/**
- * Flex time for this job. Only valid if this is a periodic job.
+ * Flex time for this job. Only valid if this is a periodic job. The job can
+ * execute at any time in a window of flex length at the end of the period.
*/
public long getFlexMillis() {
long interval = getIntervalMillis();
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 207b70a..7e67e8d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1404,6 +1404,16 @@
public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
/**
+ * Activity Action: Start the Keyboard Shortcuts Helper screen.
+ * <p>Input: Nothing.
+ * <p>Output: Nothing.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SHOW_KEYBOARD_SHORTCUTS =
+ "android.intent.action.SHOW_KEYBOARD_SHORTCUTS";
+
+ /**
* Activity Action: Show settings for managing network data usage of a
* specific application. Applications should define an activity that offers
* options to control data usage.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 104feb5..585d2a3 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -366,6 +366,9 @@
*
* <p>NOTE: {@code WebView} does not honor this flag.
*
+ * <p>This flag is ignored on Android N and above if an Android Network Security Config is
+ * present.
+ *
* <p>This flag comes from
* {@link android.R.styleable#AndroidManifestApplication_usesCleartextTraffic
* android:usesCleartextTraffic} of the <application> tag.
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 6b3d4f1..430c7e7 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -18,6 +18,7 @@
import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IOnAppsChangedListener;
import android.content.pm.ParceledListSlice;
@@ -37,7 +38,7 @@
void addOnAppsChangedListener(String callingPackage, in IOnAppsChangedListener listener);
void removeOnAppsChangedListener(in IOnAppsChangedListener listener);
ParceledListSlice getLauncherActivities(String packageName, in UserHandle user);
- ResolveInfo resolveActivity(in Intent intent, in UserHandle user);
+ ActivityInfo resolveActivity(in ComponentName component, in UserHandle user);
void startActivityAsUser(in ComponentName component, in Rect sourceBounds,
in Bundle opts, in UserHandle user);
void showAppDetailsAsUser(in ComponentName component, in Rect sourceBounds,
@@ -47,17 +48,16 @@
ApplicationInfo getApplicationInfo(String packageName, int flags, in UserHandle user);
ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName,
- in ComponentName componentName, int flags, in UserHandle user);
- ParceledListSlice getShortcutInfo(String callingPackage, String packageName, in List<String> ids,
- in UserHandle user);
+ in List shortcutIds, in ComponentName componentName, int flags, in UserHandle user);
void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
in UserHandle user);
boolean startShortcut(String callingPackage, String packageName, String id,
- in Rect sourceBounds, in Bundle startActivityOptions, in UserHandle user);
+ in Rect sourceBounds, in Bundle startActivityOptions, int userId);
- int getShortcutIconResId(String callingPackage, in ShortcutInfo shortcut, in UserHandle user);
- ParcelFileDescriptor getShortcutIconFd(String callingPackage, in ShortcutInfo shortcut,
- in UserHandle user);
+ int getShortcutIconResId(String callingPackage, String packageName, String id,
+ int userId);
+ ParcelFileDescriptor getShortcutIconFd(String callingPackage, String packageName, String id,
+ int userId);
boolean hasShortcutHostPermission(String callingPackage);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 6fce36b..0526815 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -388,6 +388,15 @@
void deleteApplicationCacheFiles(in String packageName, IPackageDataObserver observer);
/**
+ * Delete all the cache files in an applications cache directory
+ * @param packageName The package name of the application whose cache
+ * files need to be deleted
+ * @param userId the user to delete application cache for
+ * @param observer a callback used to notify when the deletion is finished.
+ */
+ void deleteApplicationCacheFilesAsUser(in String packageName, int userId, IPackageDataObserver observer);
+
+ /**
* Clear the user data directory of an application.
* @param packageName The package name of the application whose cache
* files need to be deleted
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 31d377b..9c90346 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -28,11 +28,12 @@
ParceledListSlice getDynamicShortcuts(String packageName, int userId);
- boolean addDynamicShortcut(String packageName, in ShortcutInfo shortcutInfo, int userId);
+ boolean addDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList,
+ int userId);
- void deleteDynamicShortcut(String packageName, in String shortcutId, int userId);
+ void removeDynamicShortcuts(String packageName, in List shortcutIds, int userId);
- void deleteAllDynamicShortcuts(String packageName, int userId);
+ void removeAllDynamicShortcuts(String packageName, int userId);
ParceledListSlice getPinnedShortcuts(String packageName, int userId);
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index 40e1a9f..2beca7b 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -39,7 +39,6 @@
private ActivityInfo mActivityInfo;
private ComponentName mComponentName;
- private ResolveInfo mResolveInfo;
private UserHandle mUser;
/**
@@ -49,11 +48,10 @@
* @param info ResolveInfo from which to create the LauncherActivityInfo.
* @param user The UserHandle of the profile to which this activity belongs.
*/
- LauncherActivityInfo(Context context, ResolveInfo info, UserHandle user) {
+ LauncherActivityInfo(Context context, ActivityInfo info, UserHandle user) {
this(context);
- mResolveInfo = info;
- mActivityInfo = info.activityInfo;
- mComponentName = LauncherApps.getComponentName(info);
+ mActivityInfo = info;
+ mComponentName = new ComponentName(info.packageName, info.name);
mUser = user;
}
@@ -91,7 +89,7 @@
* @return The label for the activity.
*/
public CharSequence getLabel() {
- return mResolveInfo.loadLabel(mPm);
+ return mActivityInfo.loadLabel(mPm);
}
/**
@@ -103,7 +101,7 @@
* @return The drawable associated with the activity.
*/
public Drawable getIcon(int density) {
- final int iconRes = mResolveInfo.getIconResourceInternal();
+ final int iconRes = mActivityInfo.getIconResource();
Drawable icon = null;
// Get the preferred density icon from the app's resources
if (density != 0 && iconRes != 0) {
@@ -116,7 +114,7 @@
}
// Get the default density icon
if (icon == null) {
- icon = mResolveInfo.loadIcon(mPm);
+ icon = mActivityInfo.loadIcon(mPm);
}
return icon;
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index d865f345..824722d 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -30,6 +31,7 @@
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
@@ -37,6 +39,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -205,6 +208,9 @@
String mPackage;
@Nullable
+ List<String> mShortcutIds;
+
+ @Nullable
ComponentName mActivity;
@QueryFlags
@@ -229,6 +235,14 @@
}
/**
+ * If non-null, return only the specified shortcuts by ID. When setting this field,
+ * a packange name must also be set with {@link #setPackage}.
+ */
+ public void setShortcutIds(@Nullable List<String> shortcutIds) {
+ mShortcutIds = shortcutIds;
+ }
+
+ /**
* If non-null, returns only shortcuts associated with the activity.
*/
public void setActivity(@Nullable ComponentName activity) {
@@ -250,6 +264,13 @@
mPm = context.getPackageManager();
}
+ /** @hide */
+ @TestApi
+ public LauncherApps(Context context) {
+ this(context, ILauncherApps.Stub.asInterface(
+ ServiceManager.getService(Context.LAUNCHER_APPS_SERVICE)));
+ }
+
/**
* Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and
* {@link Intent#CATEGORY_LAUNCHER}, for a specified user.
@@ -271,7 +292,7 @@
}
ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();
for (ResolveInfo ri : activities.getList()) {
- LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user);
+ LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user);
if (DEBUG) {
Log.v(TAG, "Returning activity for profile " + user + " : "
+ lai.getComponentName());
@@ -281,10 +302,6 @@
return lais;
}
- static ComponentName getComponentName(ResolveInfo ri) {
- return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);
- }
-
/**
* Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
* returns null.
@@ -295,9 +312,9 @@
*/
public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
try {
- ResolveInfo ri = mService.resolveActivity(intent, user);
- if (ri != null) {
- LauncherActivityInfo info = new LauncherActivityInfo(mContext, ri, user);
+ ActivityInfo ai = mService.resolveActivity(intent.getComponent(), user);
+ if (ai != null) {
+ LauncherActivityInfo info = new LauncherActivityInfo(mContext, ai, user);
return info;
}
} catch (RemoteException re) {
@@ -369,6 +386,7 @@
*
* @return An {@link ApplicationInfo} containing information about the package or
* null of the package isn't found.
+ * @hide
*/
public ApplicationInfo getApplicationInfo(String packageName, @ApplicationInfoFlags int flags,
UserHandle user) {
@@ -429,7 +447,8 @@
@NonNull UserHandle user) {
try {
return mService.getShortcuts(mContext.getPackageName(),
- query.mChangedSince, query.mPackage, query.mActivity, query.mQueryFlags, user)
+ query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity,
+ query.mQueryFlags, user)
.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -437,29 +456,18 @@
}
/**
- * Returns {@link ShortcutInfo}s with the given IDs from a package.
- *
- * <p>Callers must be allowed to access the shortcut information, as defined in {@link
- * #hasShortcutHostPermission()}.
- *
- * @param packageName The target package.
- * @param ids IDs of the shortcuts to retrieve.
- * @param user The UserHandle of the profile.
- *
- * @return list of {@link ShortcutInfo} associated with the package.
+ * @hide // No longer used. Use getShortcuts() instead. Kept for unit tests.
*/
@Nullable
public List<ShortcutInfo> getShortcutInfo(@NonNull String packageName,
@NonNull List<String> ids, @NonNull UserHandle user) {
- try {
- return mService.getShortcutInfo(mContext.getPackageName(), packageName, ids, user)
- .getList();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ final ShortcutQuery q = new ShortcutQuery();
+ q.setPackage(packageName);
+ q.setShortcutIds(ids);
+ q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
+ return getShortcuts(q, user);
}
-
/**
* Pin shortcuts on a package.
*
@@ -483,21 +491,24 @@
}
/**
- * Return the icon resource ID, if {@code shortcut} has one
- * (i.e. when {@link ShortcutInfo#hasIconResource()} returns {@code true}).
- *
- * <p>Callers must be allowed to access the shortcut information, as defined in {@link
- * #hasShortcutHostPermission()}.
- *
- * @param shortcut The target shortcut.
- * @param user The UserHandle of the profile.
+ * @hide kept for testing.
*/
- public int getShortcutIconResId(@NonNull ShortcutInfo shortcut, @NonNull UserHandle user) {
- try {
- return mService.getShortcutIconResId(mContext.getPackageName(), shortcut, user);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) {
+ return shortcut.getIconResourceId();
+ }
+
+ /**
+ * @hide kept for testing.
+ */
+ public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId,
+ @NonNull UserHandle user) {
+ final ShortcutQuery q = new ShortcutQuery();
+ q.setPackage(packageName);
+ q.setShortcutIds(Arrays.asList(shortcutId));
+ q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
+ final List<ShortcutInfo> shortcuts = getShortcuts(q, user);
+
+ return shortcuts.size() > 0 ? shortcuts.get(0).getIconResourceId() : 0;
}
/**
@@ -508,12 +519,34 @@
* #hasShortcutHostPermission()}.
*
* @param shortcut The target shortcut.
+ */
+ public ParcelFileDescriptor getShortcutIconFd(
+ @NonNull ShortcutInfo shortcut) {
+ return getShortcutIconFd(shortcut.getPackageName(), shortcut.getId(),
+ shortcut.getUserId());
+ }
+
+ /**
+ * Return the icon as {@link ParcelFileDescriptor}, when it's stored as a file
+ * (i.e. when {@link ShortcutInfo#hasIconFile()} returns {@code true}).
+ *
+ * <p>Callers must be allowed to access the shortcut information, as defined in {@link
+ * #hasShortcutHostPermission()}.
+ *
+ * @param packageName The target package name.
+ * @param shortcutId The ID of the shortcut to lad rom.
* @param user The UserHandle of the profile.
*/
public ParcelFileDescriptor getShortcutIconFd(
- @NonNull ShortcutInfo shortcut, @NonNull UserHandle user) {
+ @NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) {
+ return getShortcutIconFd(packageName, shortcutId, user.getIdentifier());
+ }
+
+ private ParcelFileDescriptor getShortcutIconFd(
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
try {
- return mService.getShortcutIconFd(mContext.getPackageName(), shortcut, user);
+ return mService.getShortcutIconFd(mContext.getPackageName(),
+ packageName, shortcutId, userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -536,9 +569,35 @@
public boolean startShortcut(@NonNull String packageName, @NonNull String shortcutId,
@Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
@NonNull UserHandle user) {
+ return startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions,
+ user.getIdentifier());
+ }
+
+ /**
+ * Launches a shortcut.
+ *
+ * <p>Callers must be allowed to access the shortcut information, as defined in {@link
+ * #hasShortcutHostPermission()}.
+ *
+ * @param shortcut The target shortcut.
+ * @param sourceBounds The Rect containing the source bounds of the clicked icon.
+ * @param startActivityOptions Options to pass to startActivity.
+ * @return {@code false} when the shortcut is no longer valid (e.g. the creator application
+ * has been uninstalled). {@code true} when the shortcut is still valid.
+ */
+ public boolean startShortcut(@NonNull ShortcutInfo shortcut,
+ @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) {
+ return startShortcut(shortcut.getPackageName(), shortcut.getId(),
+ sourceBounds, startActivityOptions,
+ shortcut.getUserId());
+ }
+
+ private boolean startShortcut(@NonNull String packageName, @NonNull String shortcutId,
+ @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
+ int userId) {
try {
return mService.startShortcut(mContext.getPackageName(), packageName, shortcutId,
- sourceBounds, startActivityOptions, user);
+ sourceBounds, startActivityOptions, userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 39bc783..ade2248 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4972,6 +4972,27 @@
IPackageDataObserver observer);
/**
+ * Attempts to delete the cache files associated with an application for a given user. Since
+ * this may take a little while, the result will be posted back to the given observer. A
+ * deletion will fail if the calling context lacks the
+ * {@link android.Manifest.permission#DELETE_CACHE_FILES} permission, if the named package
+ * cannot be found, or if the named package is a "system package". If {@code userId} does not
+ * belong to the calling user, the caller must have
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
+ *
+ * @param packageName The name of the package to delete
+ * @param userId the user for which the cache files needs to be deleted
+ * @param observer An observer callback to get notified when the cache file deletion is
+ * complete.
+ * {@link android.content.pm.IPackageDataObserver#onRemoveCompleted(String, boolean)}
+ * will be called when that happens. observer may be null to indicate that no
+ * callback is desired.
+ * @hide
+ */
+ public abstract void deleteApplicationCacheFilesAsUser(String packageName, int userId,
+ IPackageDataObserver observer);
+
+ /**
* Free storage by deleting LRU sorted list of cache files across
* all applications. If the currently available free storage
* on the device is greater than or equal to the requested
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 1812575a..a900015 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -33,6 +34,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
// TODO Enhance javadoc
/**
@@ -106,6 +109,11 @@
@Retention(RetentionPolicy.SOURCE)
public @interface CloneFlags {}
+ /**
+ * Shortcut category for
+ */
+ public static final String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
+
private final String mId;
@NonNull
@@ -123,6 +131,9 @@
@Nullable
private String mText;
+ @NonNull
+ private List<String> mCategories;
+
/**
* Intent *with extras removed*.
*/
@@ -153,7 +164,11 @@
@Nullable
private String mBitmapPath;
+ private final int mUserId;
+
private ShortcutInfo(Builder b) {
+ mUserId = b.mContext.getUserId();
+
mId = Preconditions.checkStringNotEmpty(b.mId, "Shortcut ID must be provided");
// Note we can't do other null checks here because SM.updateShortcuts() takes partial
@@ -163,6 +178,7 @@
mIcon = b.mIcon;
mTitle = b.mTitle;
mText = b.mText;
+ mCategories = clone(b.mCategories);
mIntent = b.mIntent;
if (mIntent != null) {
final Bundle intentExtras = mIntent.getExtras();
@@ -176,6 +192,10 @@
updateTimestamp();
}
+ private <T> ArrayList<T> clone(List<T> source) {
+ return (source == null) ? null : new ArrayList<>(source);
+ }
+
/**
* Throws if any of the mandatory fields is not set.
*
@@ -191,22 +211,26 @@
* Copy constructor.
*/
private ShortcutInfo(ShortcutInfo source, @CloneFlags int cloneFlags) {
+ mUserId = source.mUserId;
mId = source.mId;
mPackageName = source.mPackageName;
mFlags = source.mFlags;
mLastChangedTimestamp = source.mLastChangedTimestamp;
+ // Just always keep it since it's cheep.
+ mIconResourceId = source.mIconResourceId;
+
if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) {
mActivityComponent = source.mActivityComponent;
if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
mIcon = source.mIcon;
mBitmapPath = source.mBitmapPath;
- mIconResourceId = source.mIconResourceId;
}
mTitle = source.mTitle;
mText = source.mText;
+ mCategories = clone(source.mCategories);
if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) {
mIntent = source.mIntent;
mIntentPersistableExtras = source.mIntentPersistableExtras;
@@ -238,6 +262,7 @@
* @hide
*/
public void copyNonNullFieldsFrom(ShortcutInfo source) {
+ Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match");
Preconditions.checkState(mId.equals(source.mId), "ID must match");
Preconditions.checkState(mPackageName.equals(source.mPackageName),
"Package name must match");
@@ -255,6 +280,9 @@
if (source.mText != null) {
mText = source.mText;
}
+ if (source.mCategories != null) {
+ mCategories = clone(source.mCategories);
+ }
if (source.mIntent != null) {
mIntent = source.mIntent;
mIntentPersistableExtras = source.mIntentPersistableExtras;
@@ -318,6 +346,8 @@
private String mText;
+ private List<String> mCategories;
+
private Intent mIntent;
private int mWeight;
@@ -362,8 +392,9 @@
*
* <p>For performance reasons, icons will <b>NOT</b> be available on instances
* returned by {@link ShortcutManager} or {@link LauncherApps}. Launcher applications
- * need to use {@link LauncherApps#getShortcutIconFd(ShortcutInfo, UserHandle)}
- * and {@link LauncherApps#getShortcutIconResId(ShortcutInfo, UserHandle)}.
+ * can use {@link ShortcutInfo#getIconResourceId()} if {@link #hasIconResource()} is true.
+ * Otherwise, if {@link #hasIconFile()} is true, use
+ * {@link LauncherApps#getShortcutIconFd} to load the image.
*/
@NonNull
public Builder setIcon(Icon icon) {
@@ -396,6 +427,18 @@
}
/**
+ * Sets categories for a shortcut. Launcher applications may use this information to
+ * categorise shortcuts.
+ *
+ * @see #SHORTCUT_CATEGORY_CONVERSATION
+ */
+ @NonNull
+ public Builder setCategories(List<String> categories) {
+ mCategories = categories;
+ return this;
+ }
+
+ /**
* Sets the intent of a shortcut. This is a mandatory field. The extras must only contain
* persistable information. (See {@link PersistableBundle}).
*/
@@ -493,6 +536,14 @@
}
/**
+ * Return the categories.
+ */
+ @Nullable
+ public List<String> getCategories() {
+ return mCategories;
+ }
+
+ /**
* Return the intent.
*
* <p>All shortcuts must have an intent, but this method will return null when
@@ -544,6 +595,18 @@
return mExtras;
}
+ /** @hide */
+ public int getUserId() {
+ return mUserId;
+ }
+
+ /**
+ * {@link UserHandle} on which the publisher created shortcuts.
+ */
+ public UserHandle getUserHandle() {
+ return UserHandle.of(mUserId);
+ }
+
/**
* Last time when any of the fields was updated.
*/
@@ -590,7 +653,7 @@
/**
* Return whether a shortcut's icon is a resource in the owning package.
*
- * @see LauncherApps#getShortcutIconResId(ShortcutInfo, UserHandle)
+ * @see LauncherApps#getShortcutIconResId(ShortcutInfo)
*/
public boolean hasIconResource() {
return hasFlags(FLAG_HAS_ICON_RES);
@@ -599,7 +662,7 @@
/**
* Return whether a shortcut's icon is stored as a file.
*
- * @see LauncherApps#getShortcutIconFd(ShortcutInfo, UserHandle)
+ * @see LauncherApps#getShortcutIconFd(ShortcutInfo)
*/
public boolean hasIconFile() {
return hasFlags(FLAG_HAS_ICON_FILE);
@@ -643,7 +706,9 @@
mIconResourceId = iconResourceId;
}
- /** @hide */
+ /**
+ * Get the resource ID for the icon, valid only when {@link #hasIconResource()} } is true.
+ */
public int getIconResourceId() {
return mIconResourceId;
}
@@ -661,12 +726,15 @@
private ShortcutInfo(Parcel source) {
final ClassLoader cl = getClass().getClassLoader();
+ mUserId = source.readInt();
mId = source.readString();
mPackageName = source.readString();
mActivityComponent = source.readParcelable(cl);
mIcon = source.readParcelable(cl);
mTitle = source.readString();
mText = source.readString();
+ mCategories = new ArrayList<>();
+ source.readStringList(mCategories);
mIntent = source.readParcelable(cl);
mIntentPersistableExtras = source.readParcelable(cl);
mWeight = source.readInt();
@@ -679,12 +747,14 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mUserId);
dest.writeString(mId);
dest.writeString(mPackageName);
dest.writeParcelable(mActivityComponent, flags);
dest.writeParcelable(mIcon, flags);
dest.writeString(mTitle);
dest.writeString(mText);
+ dest.writeStringList(mCategories);
dest.writeParcelable(mIntent, flags);
dest.writeParcelable(mIntentPersistableExtras, flags);
dest.writeInt(mWeight);
@@ -749,6 +819,9 @@
sb.append(", text=");
sb.append(secure ? "***" : mText);
+ sb.append(", categories=");
+ sb.append(mCategories);
+
sb.append(", icon=");
sb.append(mIcon);
@@ -784,17 +857,20 @@
}
/** @hide */
- public ShortcutInfo(String id, String packageName, ComponentName activityComponent,
- Icon icon, String title, String text, Intent intent,
+ public ShortcutInfo(
+ @UserIdInt int userId, String id, String packageName, ComponentName activityComponent,
+ Icon icon, String title, String text, List<String> categories, Intent intent,
PersistableBundle intentPersistableExtras,
int weight, PersistableBundle extras, long lastChangedTimestamp,
int flags, int iconResId, String bitmapPath) {
+ mUserId = userId;
mId = id;
mPackageName = packageName;
mActivityComponent = activityComponent;
mIcon = icon;
mTitle = title;
mText = text;
+ mCategories = clone(categories);
mIntent = intent;
mIntentPersistableExtras = intentPersistableExtras;
mWeight = weight;
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index e4a98b5..75803d3 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -16,8 +16,11 @@
package android.content.pm;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.content.Context;
+import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import com.android.internal.annotations.VisibleForTesting;
@@ -31,11 +34,11 @@
* <h3>Dynamic shortcuts and pinned shortcuts</h3>
*
* An application can publish shortcuts with {@link #setDynamicShortcuts(List)} and
- * {@link #addDynamicShortcut(ShortcutInfo)}. There can be at most
+ * {@link #addDynamicShortcuts(List)}. There can be at most
* {@link #getMaxDynamicShortcutCount()} number of dynamic shortcuts at a time from the same
* application.
- * A dynamic shortcut can be deleted with {@link #deleteDynamicShortcut(String)}, and apps
- * can also use {@link #deleteAllDynamicShortcuts()} to delete all dynamic shortcuts.
+ * A dynamic shortcut can be deleted with {@link #removeDynamicShortcuts(List)}, and apps
+ * can also use {@link #removeAllDynamicShortcuts()} to delete all dynamic shortcuts.
*
* <p>The shortcuts that are currently published by the above APIs are called "dynamic", because
* they can be removed by the creator application at any time. The user may "pin" dynamic shortcuts
@@ -58,11 +61,11 @@
*
* <h3>Rate limiting</h3>
*
- * Calls to {@link #setDynamicShortcuts(List)}, {@link #addDynamicShortcut(ShortcutInfo)},
+ * Calls to {@link #setDynamicShortcuts(List)}, {@link #addDynamicShortcuts(List)},
* and {@link #updateShortcuts(List)} will be
* rate-limited. An application can call these methods at most
* {@link #getRemainingCallCount()} times until the rate-limiting counter is reset,
- * which happens at a certain time every day.
+ * which happens every hour.
*
* <p>An application can use {@link #getRateLimitResetTime()} to get the next reset time.
*
@@ -97,6 +100,15 @@
}
/**
+ * @hide
+ */
+ @TestApi
+ public ShortcutManager(Context context) {
+ this(context, IShortcutService.Stub.asInterface(
+ ServiceManager.getService(Context.SHORTCUT_SERVICE)));
+ }
+
+ /**
* Publish a list of shortcuts. All existing dynamic shortcuts from the caller application
* will be replaced.
*
@@ -141,10 +153,10 @@
* @throws IllegalArgumentException if the caller application has already published the
* max number of dynamic shortcuts.
*/
- public boolean addDynamicShortcut(@NonNull ShortcutInfo shortcutInfo) {
+ public boolean addDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
try {
- return mService.addDynamicShortcut(
- mContext.getPackageName(), shortcutInfo, injectMyUserId());
+ return mService.addDynamicShortcuts(mContext.getPackageName(),
+ new ParceledListSlice(shortcutInfoList), injectMyUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -153,9 +165,10 @@
/**
* Delete a single dynamic shortcut by ID.
*/
- public void deleteDynamicShortcut(@NonNull String shortcutId) {
+ public void removeDynamicShortcuts(@NonNull List<String> shortcutIds) {
try {
- mService.deleteDynamicShortcut(mContext.getPackageName(), shortcutId, injectMyUserId());
+ mService.removeDynamicShortcuts(mContext.getPackageName(), shortcutIds,
+ injectMyUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -164,9 +177,9 @@
/**
* Delete all dynamic shortcuts from the caller application.
*/
- public void deleteAllDynamicShortcuts() {
+ public void removeAllDynamicShortcuts() {
try {
- mService.deleteAllDynamicShortcuts(mContext.getPackageName(), injectMyUserId());
+ mService.removeAllDynamicShortcuts(mContext.getPackageName(), injectMyUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index d57f2e6e..dc3d317 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -42,15 +42,10 @@
public abstract List<ShortcutInfo>
getShortcuts(int launcherUserId,
@NonNull String callingPackage, long changedSince,
- @Nullable String packageName, @Nullable ComponentName componentName,
- @ShortcutQuery.QueryFlags int flags,
+ @Nullable String packageName, @Nullable List<String> shortcutIds,
+ @Nullable ComponentName componentName, @ShortcutQuery.QueryFlags int flags,
int userId);
- public abstract List<ShortcutInfo>
- getShortcutInfo(int launcherUserId, @NonNull String callingPackage,
- @NonNull String packageName, @Nullable List<String> ids, int userId);
-
-
public abstract boolean
isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String id, int userId);
@@ -65,11 +60,11 @@
public abstract void addListener(@NonNull ShortcutChangeListener listener);
public abstract int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
- @NonNull ShortcutInfo shortcut, int userId);
+ @NonNull String packageName, @NonNull String shortcutId, int userId);
public abstract ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
@NonNull String callingPackage,
- @NonNull ShortcutInfo shortcut, int userId);
+ @NonNull String packageName, @NonNull String shortcutId, int userId);
public abstract boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage);
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 18a155d..b542339 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -501,11 +501,8 @@
Log.d(TAG, "createCaptureSessionByOutputConfiguration");
}
- // OutputConfiguration objects aren't immutable, make a copy before using.
- List<OutputConfiguration> currentOutputs = new ArrayList<OutputConfiguration>();
- for (OutputConfiguration output : outputConfigurations) {
- currentOutputs.add(new OutputConfiguration(output));
- }
+ // OutputConfiguration objects are immutable, but need to have our own array
+ List<OutputConfiguration> currentOutputs = new ArrayList<>(outputConfigurations);
createCaptureSessionInternal(null, currentOutputs, callback, handler,
/*isConstrainedHighSpeed*/false);
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index cd0c474..61b534b 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -17,6 +17,8 @@
package android.hardware.camera2.params;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.utils.HashCodeHelpers;
@@ -71,12 +73,12 @@
public static final int ROTATION_270 = 3;
/**
- * Invalid surface set ID.
+ * Invalid surface group ID.
*
*<p>An {@link OutputConfiguration} with this value indicates that the included surface
- *doesn't belong to any surface set.</p>
+ *doesn't belong to any surface group.</p>
*/
- public static final int SURFACE_SET_ID_INVALID = -1;
+ public static final int SURFACE_GROUP_ID_NONE = -1;
/**
* Create a new {@link OutputConfiguration} instance with a {@link Surface}.
@@ -84,11 +86,47 @@
* @param surface
* A Surface for camera to output to.
*
- * <p>This constructor creates a default configuration.</p>
+ * <p>This constructor creates a default configuration, with a surface group ID of
+ * {@value #SURFACE_GROUP_ID_NONE}.</p>
*
*/
- public OutputConfiguration(Surface surface) {
- this(surface, ROTATION_0);
+ public OutputConfiguration(@NonNull Surface surface) {
+ this(SURFACE_GROUP_ID_NONE, surface, ROTATION_0);
+ }
+
+ /**
+ * Create a new {@link OutputConfiguration} instance with a {@link Surface},
+ * with a surface group ID.
+ *
+ * <p>
+ * A surface group ID is used to identify which surface group this output surface belongs to. A
+ * surface group is a group of output surfaces that are not intended to receive camera output
+ * buffer streams simultaneously. The {@link CameraDevice} may be able to share the buffers used
+ * by all the surfaces from the same surface group, therefore may reduce the overall memory
+ * footprint. The application should only set the same set ID for the streams that are not
+ * simultaneously streaming. A negative ID indicates that this surface doesn't belong to any
+ * surface group. The default value is {@value #SURFACE_GROUP_ID_NONE}.</p>
+ *
+ * <p>For example, a video chat application that has an adaptive output resolution feature would
+ * need two (or more) output resolutions, to switch resolutions without any output glitches.
+ * However, at any given time, only one output is active to minimize outgoing network bandwidth
+ * and encoding overhead. To save memory, the application should set the video outputs to have
+ * the same non-negative group ID, so that the camera device can share the same memory region
+ * for the alternating outputs.</p>
+ *
+ * <p>It is not an error to include output streams with the same group ID in the same capture
+ * request, but the resulting memory consumption may be higher than if the two streams were
+ * not in the same surface group to begin with, especially if the outputs have substantially
+ * different dimensions.</p>
+ *
+ * @param surfaceGroupId
+ * A group ID for this output, used for sharing memory between multiple outputs.
+ * @param surface
+ * A Surface for camera to output to.
+ *
+ */
+ public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface) {
+ this(surfaceGroupId, surface, ROTATION_0);
}
/**
@@ -100,9 +138,9 @@
* A Surface for camera to output to.
* @param rotation
* The desired rotation to be applied on camera output. Value must be one of
- * ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degree,
+ * ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degrees,
* application should make sure corresponding surface size has width and height
- * transposed corresponding to the width and height without rotation. For example,
+ * transposed relative to the width and height without rotation. For example,
* if application needs camera to capture 1280x720 picture and rotate it by 90 degree,
* application should set rotation to {@code ROTATION_90} and make sure the
* corresponding Surface size is 720x1280. Note that {@link CameraDevice} might
@@ -110,15 +148,43 @@
* @hide
*/
@SystemApi
- public OutputConfiguration(Surface surface, int rotation) {
+ public OutputConfiguration(@NonNull Surface surface, int rotation) {
+ this(SURFACE_GROUP_ID_NONE, surface, rotation);
+ }
+
+
+ /**
+ * Create a new {@link OutputConfiguration} instance, with rotation and a group ID.
+ *
+ * <p>This constructor takes an argument for desired camera rotation and for the surface group
+ * ID. See {@link #OutputConfiguration(int, Surface)} for details of the group ID.</p>
+ *
+ * @param surfaceGroupId
+ * A group ID for this output, used for sharing memory between multiple outputs.
+ * @param surface
+ * A Surface for camera to output to.
+ * @param rotation
+ * The desired rotation to be applied on camera output. Value must be one of
+ * ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degrees,
+ * application should make sure corresponding surface size has width and height
+ * transposed relative to the width and height without rotation. For example,
+ * if application needs camera to capture 1280x720 picture and rotate it by 90 degree,
+ * application should set rotation to {@code ROTATION_90} and make sure the
+ * corresponding Surface size is 720x1280. Note that {@link CameraDevice} might
+ * throw {@code IllegalArgumentException} if device cannot perform such rotation.
+ * @hide
+ */
+ @SystemApi
+ public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface, int rotation) {
checkNotNull(surface, "Surface must not be null");
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
- mSurfaceSetId = SURFACE_SET_ID_INVALID;
+ mSurfaceGroupId = surfaceGroupId;
mSurface = surface;
mRotation = rotation;
mConfiguredSize = SurfaceUtils.getSurfaceSize(surface);
mConfiguredFormat = SurfaceUtils.getSurfaceFormat(surface);
mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(surface);
+ mConfiguredGenerationId = surface.getGenerationId();
}
/**
@@ -129,35 +195,36 @@
*
* @hide
*/
- @SystemApi
- public OutputConfiguration(OutputConfiguration other) {
+ public OutputConfiguration(@NonNull OutputConfiguration other) {
if (other == null) {
throw new IllegalArgumentException("OutputConfiguration shouldn't be null");
}
this.mSurface = other.mSurface;
this.mRotation = other.mRotation;
- this.mSurfaceSetId = other.mSurfaceSetId;
+ this.mSurfaceGroupId = other.mSurfaceGroupId;
this.mConfiguredDataspace = other.mConfiguredDataspace;
this.mConfiguredFormat = other.mConfiguredFormat;
this.mConfiguredSize = other.mConfiguredSize;
+ this.mConfiguredGenerationId = other.mConfiguredGenerationId;
}
/**
* Create an OutputConfiguration from Parcel.
*/
- private OutputConfiguration(Parcel source) {
+ private OutputConfiguration(@NonNull Parcel source) {
int rotation = source.readInt();
int surfaceSetId = source.readInt();
Surface surface = Surface.CREATOR.createFromParcel(source);
checkNotNull(surface, "Surface must not be null");
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
- mSurfaceSetId = surfaceSetId;
+ mSurfaceGroupId = surfaceSetId;
mSurface = surface;
mRotation = rotation;
mConfiguredSize = SurfaceUtils.getSurfaceSize(mSurface);
mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurface);
mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurface);
+ mConfiguredGenerationId = mSurface.getGenerationId();
}
/**
@@ -165,6 +232,7 @@
*
* @return the {@link Surface} associated with this {@link OutputConfiguration}.
*/
+ @NonNull
public Surface getSurface() {
return mSurface;
}
@@ -183,35 +251,13 @@
}
/**
- * Set the surface set ID to this {@link OutputConfiguration}.
+ * Get the surface group ID associated with this {@link OutputConfiguration}.
*
- * <p>
- * A surface set ID is used to identify which surface set this output surface belongs to. A
- * surface set is a group of output surfaces that are not intended to receive camera output
- * buffer streams simultaneously. The {@link CameraDevice} may be able to share the buffers used
- * by all the surfaces from the same surface set, therefore may save the overall memory
- * footprint. The application should only set the same set ID for the streams that are not
- * simultaneously streaming. A negative ID indicates that this surface doesn't belong to any
- * surface set. The default value will be {@value #SURFACE_SET_ID_INVALID}.
- * </p>
- *
- * @param setId
+ * @return the surface group ID associated with this {@link OutputConfiguration}.
+ * The default value is {@value #SURFACE_GROUP_ID_NONE}.
*/
- public void setSurfaceSetId(int setId) {
- if (setId < 0) {
- setId = SURFACE_SET_ID_INVALID;
- }
- mSurfaceSetId = setId;
- }
-
- /**
- * Get the surface set Id associated with this {@link OutputConfiguration}.
- *
- * @return the surface set Id associated with this {@link OutputConfiguration}.
- * Value will be one of ROTATION_[0, 90, 180, 270]
- */
- public int getSurfaceSetId() {
- return mSurfaceSetId;
+ public int getSurfaceGroupId() {
+ return mSurfaceGroupId;
}
public static final Parcelable.Creator<OutputConfiguration> CREATOR =
@@ -244,7 +290,7 @@
throw new IllegalArgumentException("dest must not be null");
}
dest.writeInt(mRotation);
- dest.writeInt(mSurfaceSetId);
+ dest.writeInt(mSurfaceGroupId);
mSurface.writeToParcel(dest, flags);
}
@@ -265,12 +311,13 @@
return true;
} else if (obj instanceof OutputConfiguration) {
final OutputConfiguration other = (OutputConfiguration) obj;
- return mSurface == other.mSurface &&
- mRotation == other.mRotation &&
+ return mRotation == other.mRotation &&
+ mSurface == other.mSurface &&
+ mConfiguredGenerationId == other.mConfiguredGenerationId &&
mConfiguredSize.equals(other.mConfiguredSize) &&
mConfiguredFormat == other.mConfiguredFormat &&
mConfiguredDataspace == other.mConfiguredDataspace &&
- mSurfaceSetId == other.mSurfaceSetId;
+ mSurfaceGroupId == other.mSurfaceGroupId;
}
return false;
}
@@ -280,16 +327,20 @@
*/
@Override
public int hashCode() {
- return HashCodeHelpers.hashCode(mSurface.hashCode(), mRotation);
+ return HashCodeHelpers.hashCode(
+ mRotation, mSurface.hashCode(), mConfiguredGenerationId,
+ mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace, mSurfaceGroupId);
}
private static final String TAG = "OutputConfiguration";
private final Surface mSurface;
private final int mRotation;
- private int mSurfaceSetId;
+ private int mSurfaceGroupId;
// The size, format, and dataspace of the surface when OutputConfiguration is created.
private final Size mConfiguredSize;
private final int mConfiguredFormat;
private final int mConfiguredDataspace;
+ // Surface generation ID to distinguish changes to Surface native internals
+ private final int mConfiguredGenerationId;
}
diff --git a/core/java/android/hardware/location/NanoApp.java b/core/java/android/hardware/location/NanoApp.java
index c8f3439..03ac4a2 100644
--- a/core/java/android/hardware/location/NanoApp.java
+++ b/core/java/android/hardware/location/NanoApp.java
@@ -18,6 +18,7 @@
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
/** A class describing nano apps.
* A nano app is a piece of executable code that can be
@@ -31,10 +32,15 @@
*/
@SystemApi
public class NanoApp {
+ private final String TAG = "NanoApp";
+
+ private final String UNKNOWN = "Unknown";
+
private String mPublisher;
private String mName;
private int mAppId;
+ private boolean mAppIdSet;
private int mAppVersion;
private int mNeededReadMemBytes;
@@ -45,7 +51,48 @@
private int[] mOutputEvents;
private byte[] mAppBinary;
+ /**
+ * If this version of the constructor is used, the methods
+ * {@link #setAppBinary(byte[])} and {@link #setAppId(int)} must be called
+ * prior to passing this object to any managers.
+ *
+ * @see #NanoApp(int, byte[])
+ */
public NanoApp() {
+ this(0, null);
+ mAppIdSet = false;
+ }
+
+ /**
+ * Initialize a NanoApp with the given id and binary.
+ *
+ * While this sets defaults for other fields, users will want to provide
+ * other values for those fields in most cases.
+ *
+ * @see #setPublisher(String)
+ * @see #setName(String)
+ * @see #setAppVersion(int)
+ * @see #setNeededReadMemBytes(int)
+ * @see #setNeededWriteMemBytes(int)
+ * @see #setNeededExecMemBytes(int)
+ * @see #setNeededSensors(int[])
+ * @see #setOutputEvents(int[])
+ */
+ public NanoApp(int appId, byte[] appBinary) {
+ mPublisher = UNKNOWN;
+ mName = UNKNOWN;
+
+ mAppId = appId;
+ mAppIdSet = true;
+ mAppVersion = 0;
+
+ mNeededReadMemBytes = 0;
+ mNeededWriteMemBytes = 0;
+ mNeededExecMemBytes = 0;
+
+ mNeededSensors = new int[0];
+ mOutputEvents = new int[0];
+ mAppBinary = appBinary;
}
/**
@@ -73,6 +120,7 @@
*/
public void setAppId(int appId) {
mAppId = appId;
+ mAppIdSet = true;
}
/**
@@ -144,7 +192,7 @@
* @return publisher name
*/
public String getPublisher() {
- return mPublisher;
+ return mPublisher;
}
/**
@@ -153,7 +201,7 @@
* @return app name
*/
public String getName() {
- return mName;
+ return mName;
}
/**
@@ -162,7 +210,7 @@
* @return identifier for this app
*/
public int getAppId() {
- return mAppId;
+ return mAppId;
}
/**
@@ -171,7 +219,7 @@
* @return app version
*/
public int getAppVersion() {
- return mAppVersion;
+ return mAppVersion;
}
/**
@@ -180,7 +228,7 @@
* @return readable memory needed in bytes
*/
public int getNeededReadMemBytes() {
- return mNeededReadMemBytes;
+ return mNeededReadMemBytes;
}
/**
@@ -189,7 +237,7 @@
* @return writable memory needed in bytes
*/
public int getNeededWriteMemBytes() {
- return mNeededWriteMemBytes;
+ return mNeededWriteMemBytes;
}
/**
@@ -198,7 +246,7 @@
* @return executable memory needed in bytes
*/
public int getNeededExecMemBytes() {
- return mNeededExecMemBytes;
+ return mNeededExecMemBytes;
}
/**
@@ -207,7 +255,7 @@
* @return sensors needed
*/
public int[] getNeededSensors() {
- return mNeededSensors;
+ return mNeededSensors;
}
/**
@@ -216,7 +264,7 @@
* @return generated events
*/
public int[] getOutputEvents() {
- return mOutputEvents;
+ return mOutputEvents;
}
/**
@@ -225,7 +273,7 @@
* @return app binary
*/
public byte[] getAppBinary() {
- return mAppBinary;
+ return mAppBinary;
}
private NanoApp(Parcel in) {
@@ -256,6 +304,13 @@
}
public void writeToParcel(Parcel out, int flags) {
+ if (mAppBinary == null) {
+ throw new IllegalStateException("Must set non-null AppBinary for nanoapp " + mName);
+ }
+ if (!mAppIdSet) {
+ throw new IllegalStateException("Must set AppId for nanoapp " + mName);
+ }
+
out.writeString(mPublisher);
out.writeString(mName);
out.writeInt(mAppId);
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
index cc018e9..458c584 100644
--- a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
+++ b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
@@ -35,9 +35,11 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
-import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
/**
* Enrollment information about the different available keyphrases.
@@ -82,8 +84,16 @@
public static final String EXTRA_VOICE_KEYPHRASE_LOCALE =
"com.android.intent.extra.VOICE_KEYPHRASE_LOCALE";
- private KeyphraseMetadata[] mKeyphrases;
- private String mEnrollmentPackage;
+ /**
+ * List of available keyphrases.
+ */
+ final private KeyphraseMetadata[] mKeyphrases;
+
+ /**
+ * Map between KeyphraseMetadata and the package name of the enrollment app that provides it.
+ */
+ final private Map<KeyphraseMetadata, String> mKeyphrasePackageMap;
+
private String mParseError;
public KeyphraseEnrollmentInfo(PackageManager pm) {
@@ -94,15 +104,17 @@
new Intent(ACTION_MANAGE_VOICE_KEYPHRASES), PackageManager.MATCH_DEFAULT_ONLY);
if (ris == null || ris.isEmpty()) {
// No application capable of enrolling for voice keyphrases is present.
- mParseError = "No enrollment application found";
+ mParseError = "No enrollment applications found";
+ mKeyphrasePackageMap = null;
+ mKeyphrases = null;
return;
}
- boolean found = false;
- ApplicationInfo ai = null;
+ List<String> parseErrors = new LinkedList<String>();
+ mKeyphrasePackageMap = new HashMap<KeyphraseMetadata, String>();
for (ResolveInfo ri : ris) {
try {
- ai = pm.getApplicationInfo(
+ ApplicationInfo ai = pm.getApplicationInfo(
ri.activityInfo.packageName, PackageManager.GET_META_DATA);
if ((ai.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) == 0) {
// The application isn't privileged (/system/priv-app).
@@ -116,27 +128,45 @@
Slog.w(TAG, ai.packageName + " does not require MANAGE_VOICE_KEYPHRASES");
continue;
}
- mEnrollmentPackage = ai.packageName;
- found = true;
- break;
+
+ mKeyphrasePackageMap.put(
+ getKeyphraseMetadataFromApplicationInfo(pm, ai, parseErrors),
+ ai.packageName);
} catch (PackageManager.NameNotFoundException e) {
- Slog.w(TAG, "error parsing voice enrollment meta-data", e);
+ String error = "error parsing voice enrollment meta-data for "
+ + ri.activityInfo.packageName;
+ parseErrors.add(error + ": " + e);
+ Slog.w(TAG, error, e);
}
}
- if (!found) {
+ if (mKeyphrasePackageMap.isEmpty()) {
+ String error = "No suitable enrollment application found";
+ parseErrors.add(error);
+ Slog.w(TAG, error);
mKeyphrases = null;
- mParseError = "No suitable enrollment application found";
- return;
+ } else {
+ mKeyphrases = mKeyphrasePackageMap.keySet().toArray(
+ new KeyphraseMetadata[mKeyphrasePackageMap.size()]);
}
+ if (!parseErrors.isEmpty()) {
+ mParseError = TextUtils.join("\n", parseErrors);
+ }
+ }
+
+ private KeyphraseMetadata getKeyphraseMetadataFromApplicationInfo(PackageManager pm,
+ ApplicationInfo ai, List<String> parseErrors) {
XmlResourceParser parser = null;
+ String packageName = ai.packageName;
+ KeyphraseMetadata keyphraseMetadata = null;
try {
parser = ai.loadXmlMetaData(pm, VOICE_KEYPHRASE_META_DATA);
if (parser == null) {
- mParseError = "No " + VOICE_KEYPHRASE_META_DATA + " meta-data for "
- + ai.packageName;
- return;
+ String error = "No " + VOICE_KEYPHRASE_META_DATA + " meta-data for " + packageName;
+ parseErrors.add(error);
+ Slog.w(TAG, error);
+ return null;
}
Resources res = pm.getResourcesForApplication(ai);
@@ -149,48 +179,55 @@
String nodeName = parser.getName();
if (!"voice-enrollment-application".equals(nodeName)) {
- mParseError = "Meta-data does not start with voice-enrollment-application tag";
- return;
+ String error = "Meta-data does not start with voice-enrollment-application tag for "
+ + packageName;
+ parseErrors.add(error);
+ Slog.w(TAG, error);
+ return null;
}
TypedArray array = res.obtainAttributes(attrs,
com.android.internal.R.styleable.VoiceEnrollmentApplication);
- initializeKeyphrasesFromTypedArray(array);
+ keyphraseMetadata = getKeyphraseFromTypedArray(array, packageName, parseErrors);
array.recycle();
} catch (XmlPullParserException e) {
- mParseError = "Error parsing keyphrase enrollment meta-data: " + e;
- Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e);
- return;
+ String error = "Error parsing keyphrase enrollment meta-data for " + packageName;
+ parseErrors.add(error + ": " + e);
+ Slog.w(TAG, error, e);
} catch (IOException e) {
- mParseError = "Error parsing keyphrase enrollment meta-data: " + e;
- Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e);
- return;
+ String error = "Error parsing keyphrase enrollment meta-data for " + packageName;
+ parseErrors.add(error + ": " + e);
+ Slog.w(TAG, error, e);
} catch (PackageManager.NameNotFoundException e) {
- mParseError = "Error parsing keyphrase enrollment meta-data: " + e;
- Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e);
- return;
+ String error = "Error parsing keyphrase enrollment meta-data for " + packageName;
+ parseErrors.add(error + ": " + e);
+ Slog.w(TAG, error, e);
} finally {
if (parser != null) parser.close();
}
+ return keyphraseMetadata;
}
- private void initializeKeyphrasesFromTypedArray(TypedArray array) {
+ private KeyphraseMetadata getKeyphraseFromTypedArray(TypedArray array, String packageName,
+ List<String> parseErrors) {
// Get the keyphrase ID.
int searchKeyphraseId = array.getInt(
com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphraseId, -1);
if (searchKeyphraseId <= 0) {
- mParseError = "No valid searchKeyphraseId specified in meta-data";
- Slog.w(TAG, mParseError);
- return;
+ String error = "No valid searchKeyphraseId specified in meta-data for " + packageName;
+ parseErrors.add(error);
+ Slog.w(TAG, error);
+ return null;
}
// Get the keyphrase text.
String searchKeyphrase = array.getString(
com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphrase);
if (searchKeyphrase == null) {
- mParseError = "No valid searchKeyphrase specified in meta-data";
- Slog.w(TAG, mParseError);
- return;
+ String error = "No valid searchKeyphrase specified in meta-data for " + packageName;
+ parseErrors.add(error);
+ Slog.w(TAG, error);
+ return null;
}
// Get the supported locales.
@@ -198,9 +235,11 @@
com.android.internal.R.styleable
.VoiceEnrollmentApplication_searchKeyphraseSupportedLocales);
if (searchKeyphraseSupportedLocales == null) {
- mParseError = "No valid searchKeyphraseSupportedLocales specified in meta-data";
- Slog.w(TAG, mParseError);
- return;
+ String error = "No valid searchKeyphraseSupportedLocales specified in meta-data for "
+ + packageName;
+ parseErrors.add(error);
+ Slog.w(TAG, error);
+ return null;
}
ArraySet<Locale> locales = new ArraySet<>();
// Try adding locales if the locale string is non-empty.
@@ -214,9 +253,11 @@
// We catch a generic exception here because we don't want the system service
// to be affected by a malformed metadata because invalid locales were specified
// by the system application.
- mParseError = "Error reading searchKeyphraseSupportedLocales from meta-data";
- Slog.w(TAG, mParseError, ex);
- return;
+ String error = "Error reading searchKeyphraseSupportedLocales from meta-data for "
+ + packageName;
+ parseErrors.add(error);
+ Slog.w(TAG, error);
+ return null;
}
}
@@ -224,13 +265,13 @@
int recognitionModes = array.getInt(com.android.internal.R.styleable
.VoiceEnrollmentApplication_searchKeyphraseRecognitionFlags, -1);
if (recognitionModes < 0) {
- mParseError = "No valid searchKeyphraseRecognitionFlags specified in meta-data";
- Slog.w(TAG, mParseError);
- return;
+ String error = "No valid searchKeyphraseRecognitionFlags specified in meta-data for "
+ + packageName;
+ parseErrors.add(error);
+ Slog.w(TAG, error);
+ return null;
}
- mKeyphrases = new KeyphraseMetadata[1];
- mKeyphrases[0] = new KeyphraseMetadata(searchKeyphraseId, searchKeyphrase, locales,
- recognitionModes);
+ return new KeyphraseMetadata(searchKeyphraseId, searchKeyphrase, locales, recognitionModes);
}
public String getParseError() {
@@ -259,14 +300,15 @@
* given keyphrase/locale combination isn't possible.
*/
public Intent getManageKeyphraseIntent(int action, String keyphrase, Locale locale) {
- if (mEnrollmentPackage == null || mEnrollmentPackage.isEmpty()) {
+ if (mKeyphrasePackageMap == null || mKeyphrasePackageMap.isEmpty()) {
Slog.w(TAG, "No enrollment application exists");
return null;
}
- if (getKeyphraseMetadata(keyphrase, locale) != null) {
+ KeyphraseMetadata keyphraseMetadata = getKeyphraseMetadata(keyphrase, locale);
+ if (keyphraseMetadata != null) {
Intent intent = new Intent(ACTION_MANAGE_VOICE_KEYPHRASES)
- .setPackage(mEnrollmentPackage)
+ .setPackage(mKeyphrasePackageMap.get(keyphraseMetadata))
.putExtra(EXTRA_VOICE_KEYPHRASE_HINT_TEXT, keyphrase)
.putExtra(EXTRA_VOICE_KEYPHRASE_LOCALE, locale.toLanguageTag())
.putExtra(EXTRA_VOICE_KEYPHRASE_ACTION, action);
@@ -298,14 +340,13 @@
return keyphraseMetadata;
}
}
- Slog.w(TAG, "Enrollment application doesn't support the given keyphrase/locale");
+ Slog.w(TAG, "No Enrollment application supports the given keyphrase/locale");
return null;
}
@Override
public String toString() {
- return "KeyphraseEnrollmentInfo [Keyphrases=" + Arrays.toString(mKeyphrases)
- + ", EnrollmentPackage=" + mEnrollmentPackage + ", ParseError=" + mParseError
- + "]";
+ return "KeyphraseEnrollmentInfo [Keyphrases=" + mKeyphrasePackageMap.toString()
+ + ", ParseError=" + mParseError + "]";
}
}
diff --git a/core/java/android/inputmethodservice/CompactExtractEditLayout.java b/core/java/android/inputmethodservice/CompactExtractEditLayout.java
new file mode 100644
index 0000000..35c54b2
--- /dev/null
+++ b/core/java/android/inputmethodservice/CompactExtractEditLayout.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2016 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.inputmethodservice;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.annotation.FractionRes;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+/**
+ * A special purpose layout for the editor extract view for tiny (sub 250dp) screens.
+ * The layout is based on sizes proportional to screen pixel size to provide for the
+ * best layout fidelity on varying pixel sizes and densities.
+ *
+ * @hide
+ */
+public class CompactExtractEditLayout extends LinearLayout {
+ private View mInputExtractEditText;
+ private View mInputExtractAccessories;
+ private View mInputExtractAction;
+ private boolean mPerformLayoutChanges;
+
+ public CompactExtractEditLayout(Context context) {
+ super(context);
+ }
+
+ public CompactExtractEditLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CompactExtractEditLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mInputExtractEditText = findViewById(com.android.internal.R.id.inputExtractEditText);
+ mInputExtractAccessories = findViewById(com.android.internal.R.id.inputExtractAccessories);
+ mInputExtractAction = findViewById(com.android.internal.R.id.inputExtractAction);
+
+ if (mInputExtractEditText != null && mInputExtractAccessories != null
+ && mInputExtractAction != null) {
+ mPerformLayoutChanges = true;
+ }
+ }
+
+ private int applyFractionInt(@FractionRes int fraction, int whole) {
+ return Math.round(getResources().getFraction(fraction, whole, whole));
+ }
+
+ private static void setLayoutHeight(View v, int px) {
+ ViewGroup.LayoutParams lp = v.getLayoutParams();
+ lp.height = px;
+ v.setLayoutParams(lp);
+ }
+
+ private static void setLayoutMarginBottom(View v, int px) {
+ ViewGroup.MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
+ lp.bottomMargin = px;
+ v.setLayoutParams(lp);
+ }
+
+ private void applyProportionalLayout(int screenWidthPx, int screenHeightPx) {
+ if (getResources().getConfiguration().isScreenRound()) {
+ setGravity(Gravity.BOTTOM);
+ }
+ setLayoutHeight(this, applyFractionInt(
+ com.android.internal.R.fraction.input_extract_layout_height, screenHeightPx));
+
+ setPadding(
+ applyFractionInt(com.android.internal.R.fraction.input_extract_layout_padding_left,
+ screenWidthPx),
+ 0,
+ applyFractionInt(com.android.internal.R.fraction.input_extract_layout_padding_right,
+ screenWidthPx),
+ 0);
+
+ setLayoutMarginBottom(mInputExtractEditText,
+ applyFractionInt(com.android.internal.R.fraction.input_extract_text_margin_bottom,
+ screenHeightPx));
+
+ setLayoutMarginBottom(mInputExtractAccessories,
+ applyFractionInt(com.android.internal.R.fraction.input_extract_action_margin_bottom,
+ screenHeightPx));
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (mPerformLayoutChanges) {
+ Resources res = getResources();
+ DisplayMetrics dm = res.getDisplayMetrics();
+ int heightPixels = dm.heightPixels;
+ int widthPixels = dm.widthPixels;
+ applyProportionalLayout(widthPixels, heightPixels);
+ }
+ }
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index cc201bc..085b97c 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -68,9 +68,10 @@
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
-import android.widget.Button;
import android.widget.FrameLayout;
+import android.widget.ImageButton;
import android.widget.LinearLayout;
+import android.widget.TextView;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -302,7 +303,7 @@
boolean mExtractViewHidden;
ExtractEditText mExtractEditText;
ViewGroup mExtractAccessories;
- Button mExtractAction;
+ View mExtractAction;
ExtractedText mExtractedText;
int mExtractedToken;
@@ -1344,7 +1345,7 @@
mExtractEditText = (ExtractEditText)view.findViewById(
com.android.internal.R.id.inputExtractEditText);
mExtractEditText.setIME(this);
- mExtractAction = (Button)view.findViewById(
+ mExtractAction = view.findViewById(
com.android.internal.R.id.inputExtractAction);
if (mExtractAction != null) {
mExtractAccessories = (ViewGroup)view.findViewById(
@@ -2408,7 +2409,35 @@
return getText(com.android.internal.R.string.ime_action_default);
}
}
-
+
+ /**
+ * Return a drawable resource id that can be used as a button icon for the given
+ * {@link EditorInfo#imeOptions EditorInfo.imeOptions}.
+ *
+ * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
+ *
+ * @return Returns a drawable resource id to use.
+ */
+ @DrawableRes
+ private int getIconForImeAction(int imeOptions) {
+ switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
+ case EditorInfo.IME_ACTION_GO:
+ return com.android.internal.R.drawable.ic_input_extract_action_go;
+ case EditorInfo.IME_ACTION_SEARCH:
+ return com.android.internal.R.drawable.ic_input_extract_action_search;
+ case EditorInfo.IME_ACTION_SEND:
+ return com.android.internal.R.drawable.ic_input_extract_action_send;
+ case EditorInfo.IME_ACTION_NEXT:
+ return com.android.internal.R.drawable.ic_input_extract_action_next;
+ case EditorInfo.IME_ACTION_DONE:
+ return com.android.internal.R.drawable.ic_input_extract_action_done;
+ case EditorInfo.IME_ACTION_PREVIOUS:
+ return com.android.internal.R.drawable.ic_input_extract_action_previous;
+ default:
+ return com.android.internal.R.drawable.ic_input_extract_action_return;
+ }
+ }
+
/**
* Called when the fullscreen-mode extracting editor info has changed,
* to determine whether the extracting (extract text and candidates) portion
@@ -2459,10 +2488,20 @@
if (hasAction) {
mExtractAccessories.setVisibility(View.VISIBLE);
if (mExtractAction != null) {
- if (ei.actionLabel != null) {
- mExtractAction.setText(ei.actionLabel);
+ if (mExtractAction instanceof ImageButton) {
+ ((ImageButton) mExtractAction)
+ .setImageResource(getIconForImeAction(ei.imeOptions));
+ if (ei.actionLabel != null) {
+ mExtractAction.setContentDescription(ei.actionLabel);
+ } else {
+ mExtractAction.setContentDescription(getTextForImeAction(ei.imeOptions));
+ }
} else {
- mExtractAction.setText(getTextForImeAction(ei.imeOptions));
+ if (ei.actionLabel != null) {
+ ((TextView) mExtractAction).setText(ei.actionLabel);
+ } else {
+ ((TextView) mExtractAction).setText(getTextForImeAction(ei.imeOptions));
+ }
}
mExtractAction.setOnClickListener(mActionClickListener);
}
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 20c2168..2dacf8f 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -120,12 +120,17 @@
* either a bad network configuration (no internet link) or captive portal.
*
* arg1 = either {@code VALID_NETWORK} or {@code INVALID_NETWORK}
+ * obj = Bundle containing map from {@code REDIRECT_URL_KEY} to {@code String}
+ * representing URL that Internet probe was redirect to, if it was redirected,
+ * or mapping to {@code null} otherwise.
*/
public static final int CMD_REPORT_NETWORK_STATUS = BASE + 7;
public static final int VALID_NETWORK = 1;
public static final int INVALID_NETWORK = 2;
+ public static String REDIRECT_URL_KEY = "redirect URL";
+
/**
* Sent by the NetworkAgent to ConnectivityService to indicate this network was
* explicitly selected. This should be sent before the NetworkInfo is marked
@@ -283,11 +288,12 @@
break;
}
case CMD_REPORT_NETWORK_STATUS: {
+ String redirectUrl = ((Bundle)msg.obj).getString(REDIRECT_URL_KEY);
if (VDBG) {
log("CMD_REPORT_NETWORK_STATUS(" +
- (msg.arg1 == VALID_NETWORK ? "VALID)" : "INVALID)"));
+ (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ") + redirectUrl);
}
- networkStatus(msg.arg1);
+ networkStatus(msg.arg1, redirectUrl);
break;
}
case CMD_SAVE_ACCEPT_UNVALIDATED: {
@@ -443,8 +449,12 @@
*
* This may be called multiple times as the network status changes and may
* generate false negatives if we lose ip connectivity before the link is torn down.
+ *
+ * @param status one of {@code VALID_NETWORK} or {@code INVALID_NETWORK}.
+ * @param redirectUrl If the Internet probe was redirected, this is the destination it was
+ * redirected to, otherwise {@code null}.
*/
- protected void networkStatus(int status) {
+ protected void networkStatus(int status, String redirectUrl) {
}
/**
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index cfd0468..1ac9fca 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -228,7 +228,6 @@
* statistics parameters.
*
* @see #setThreadStatsTag(int)
- * @see #setThreadStatsUid(int)
*/
public static void tagSocket(Socket socket) throws SocketException {
SocketTagger.get().tag(socket);
@@ -249,7 +248,6 @@
* parameters.
*
* @see #setThreadStatsTag(int)
- * @see #setThreadStatsUid(int)
*/
public static void tagDatagramSocket(DatagramSocket socket) throws SocketException {
SocketTagger.get().tag(socket);
diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java
index 6729347..87a0b70 100644
--- a/core/java/android/net/http/X509TrustManagerExtensions.java
+++ b/core/java/android/net/http/X509TrustManagerExtensions.java
@@ -44,6 +44,7 @@
private final X509TrustManager mTrustManager;
private final Method mCheckServerTrusted;
private final Method mIsUserAddedCertificate;
+ private final Method mIsSameTrustConfiguration;
/**
* Constructs a new X509TrustManagerExtensions wrapper.
@@ -57,6 +58,7 @@
mTrustManager = null;
mCheckServerTrusted = null;
mIsUserAddedCertificate = null;
+ mIsSameTrustConfiguration = null;
return;
}
// Use duck typing if possible.
@@ -80,6 +82,15 @@
throw new IllegalArgumentException(
"Required method isUserAddedCertificate(X509Certificate) missing");
}
+ // Get the option isSameTrustConfiguration method.
+ Method isSameTrustConfiguration = null;
+ try {
+ isSameTrustConfiguration = tm.getClass().getMethod("isSameTrustConfiguration",
+ String.class,
+ String.class);
+ } catch (ReflectiveOperationException ignored) {
+ }
+ mIsSameTrustConfiguration = isSameTrustConfiguration;
}
/**
@@ -150,6 +161,19 @@
*/
@SystemApi
public boolean isSameTrustConfiguration(String hostname1, String hostname2) {
- return true;
+ if (mIsSameTrustConfiguration == null) {
+ return true;
+ }
+ try {
+ return (Boolean) mIsSameTrustConfiguration.invoke(mTrustManager, hostname1, hostname2);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Failed to call isSameTrustConfiguration", e);
+ } catch (InvocationTargetException e) {
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ } else {
+ throw new RuntimeException("isSameTrustConfiguration failed", e.getCause());
+ }
+ }
}
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index c452837..773e7dd 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -3094,14 +3094,8 @@
dumpControllerActivityLine(pw, uid, category, WIFI_CONTROLLER_DATA,
u.getWifiControllerActivity(), which);
- // Dump Bluetooth scan data, per UID.
- final long bleScanTimeUs = u.getBluetoothScanTimer().getTotalTimeLocked(
+ dumpTimer(pw, uid, category, BLUETOOTH_MISC_DATA, u.getBluetoothScanTimer(),
rawRealtime, which);
- final int bleScanCount = u.getBluetoothScanTimer().getCountLocked(which);
- if (bleScanTimeUs != 0 || bleScanCount != 0) {
- dumpLine(pw, uid, category, BLUETOOTH_MISC_DATA,
- bleScanTimeUs / 1000, bleScanCount);
- }
dumpControllerActivityLine(pw, uid, category, BLUETOOTH_CONTROLLER_DATA,
u.getBluetoothControllerActivity(), which);
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index c38bf3c..67d3959 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -45,6 +45,7 @@
UserInfo getPrimaryUser();
List<UserInfo> getUsers(boolean excludeDying);
List<UserInfo> getProfiles(int userHandle, boolean enabledOnly);
+ int[] getProfileIds(int userId, boolean enabledOnly);
boolean canAddMoreManagedProfiles(int userHandle, boolean allowedToRemoveOne);
UserInfo getProfileParent(int userHandle);
boolean isSameProfileGroup(int userHandle, int otherUserHandle);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 707d5f5..d5b3b35 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1590,18 +1590,46 @@
* @return A non-empty list of UserHandles associated with the calling user.
*/
public List<UserHandle> getUserProfiles() {
- ArrayList<UserHandle> profiles = new ArrayList<UserHandle>();
- List<UserInfo> users;
+ int[] userIds = getProfileIds(UserHandle.myUserId(), true /* enabledOnly */);
+ List<UserHandle> result = new ArrayList<>(userIds.length);
+ for (int userId : userIds) {
+ result.add(UserHandle.of(userId));
+ }
+ return result;
+ }
+
+ /**
+ * Returns a list of ids for profiles associated with the specified user including the user
+ * itself.
+ *
+ * @param userId id of the user to return profiles for
+ * @param enabledOnly whether return only {@link UserInfo#isEnabled() enabled} profiles
+ * @return A non-empty list of ids of profiles associated with the specified user.
+ *
+ * @hide
+ */
+ public int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly) {
try {
- users = mService.getProfiles(UserHandle.myUserId(), true /* enabledOnly */);
+ return mService.getProfileIds(userId, enabledOnly);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
- for (UserInfo info : users) {
- UserHandle userHandle = new UserHandle(info.id);
- profiles.add(userHandle);
- }
- return profiles;
+ }
+
+ /**
+ * @see #getProfileIds(int, boolean)
+ * @hide
+ */
+ public int[] getProfileIdsWithDisabled(@UserIdInt int userId) {
+ return getProfileIds(userId, false /* enabledOnly */);
+ }
+
+ /**
+ * @see #getProfileIds(int, boolean)
+ * @hide
+ */
+ public int[] getEnabledProfileIds(@UserIdInt int userId) {
+ return getProfileIds(userId, true /* enabledOnly */);
}
/**
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index ece1228..0a8fdd9 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -824,7 +824,9 @@
}
}
- /** {@hide} */
+ /**
+ * Return the {@link StorageVolume} that contains the given file, or {@code null} if none.
+ */
public @Nullable StorageVolume getStorageVolume(File file) {
return getStorageVolume(getVolumeList(), file);
}
@@ -836,9 +838,13 @@
/** {@hide} */
private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) {
+ if (file == null) {
+ return null;
+ }
try {
file = file.getCanonicalFile();
} catch (IOException ignored) {
+ Slog.d(TAG, "Could not get canonical path for " + file);
return null;
}
for (StorageVolume volume : volumes) {
@@ -1056,8 +1062,8 @@
}
/**
- * Return if data stored at the given path will be encrypted while at rest.
- * This can help apps avoid the overhead of double-encrypting data.
+ * Return if data stored at or under the given path will be encrypted while
+ * at rest. This can help apps avoid the overhead of double-encrypting data.
*/
public boolean isEncrypted(File file) {
if (FileUtils.contains(Environment.getDataDirectory(), file)) {
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 89ac27c9a..de19f81 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -283,7 +283,7 @@
*
* <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#M M} and above
* and declares as using the {@link android.Manifest.permission#CAMERA} permission which
- * is not granted, then atempting to use this action will result in a {@link
+ * is not granted, then attempting to use this action will result in a {@link
* java.lang.SecurityException}.
*
* @see #EXTRA_OUTPUT
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2a3c3fe..db5b07a 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1581,6 +1581,8 @@
public static final class System extends NameValueTable {
public static final String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
+ private static final float DEFAULT_FONT_SCALE = 1.0f;
+
/** @hide */
public static interface Validator {
public boolean validate(String value);
@@ -2089,9 +2091,9 @@
public static void getConfigurationForUser(ContentResolver cr, Configuration outConfig,
int userHandle) {
outConfig.fontScale = Settings.System.getFloatForUser(
- cr, FONT_SCALE, outConfig.fontScale, userHandle);
+ cr, FONT_SCALE, DEFAULT_FONT_SCALE, userHandle);
if (outConfig.fontScale < 0) {
- outConfig.fontScale = 1;
+ outConfig.fontScale = DEFAULT_FONT_SCALE;
}
outConfig.setLocales(LocaleList.forLanguageTags(
Settings.System.getStringForUser(cr, SYSTEM_LOCALES, userHandle)));
@@ -6666,6 +6668,17 @@
public static final String DEVICE_PROVISIONED = "device_provisioned";
/**
+ * Whether mobile data should be allowed while the device is being provisioned.
+ * This allows the provisioning process to turn off mobile data before the user
+ * has an opportunity to set things up, preventing other processes from burning
+ * precious bytes before wifi is setup.
+ * (0 = false, 1 = true)
+ * @hide
+ */
+ public static final String DEVICE_PROVISIONING_MOBILE_DATA_ENABLED =
+ "device_provisioning_mobile_data";
+
+ /**
* The saved value for WindowManagerService.setForcedDisplaySize().
* Two integers separated by a comma. If unset, then use the real display size.
* @hide
@@ -7782,13 +7795,13 @@
* ShortcutManager specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
*
- * "reset_interval_sec=86400,max_daily_updates=5"
+ * "reset_interval_sec=86400,max_updates_per_interval=1"
*
* The following keys are supported:
*
* <pre>
* reset_interval_sec (long)
- * max_daily_updates (int)
+ * max_updates_per_interval (int)
* max_icon_dimension_dp (int, DP)
* max_icon_dimension_dp_lowram (int, DP)
* max_shortcuts (int)
diff --git a/core/java/android/security/net/config/RootTrustManager.java b/core/java/android/security/net/config/RootTrustManager.java
index 19f6887..859e022 100644
--- a/core/java/android/security/net/config/RootTrustManager.java
+++ b/core/java/android/security/net/config/RootTrustManager.java
@@ -148,4 +148,15 @@
NetworkSecurityConfig config = mConfig.getConfigForHostname("");
return config.getTrustManager().getAcceptedIssuers();
}
+
+ /**
+ * Returns {@code true} if this trust manager uses the same trust configuration for the provided
+ * hostnames.
+ *
+ * <p>This is required by android.net.http.X509TrustManagerExtensions.
+ */
+ public boolean isSameTrustConfiguration(String hostname1, String hostname2) {
+ return mConfig.getConfigForHostname(hostname1)
+ .equals(mConfig.getConfigForHostname(hostname2));
+ }
}
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index 3564e11..e93e58d 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -48,6 +48,9 @@
implements KeyListener {
/* package */ static final Object OLD_SEL_START = new NoCopySpan.Concrete();
+ private static final int LINE_FEED = 0x0A;
+ private static final int CARRIAGE_RETURN = 0x0D;
+
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -110,34 +113,37 @@
// Initial state
final int STATE_START = 0;
+ // The offset is immediately before line feed.
+ final int STATE_LF = 1;
+
// The offset is immediately before a KEYCAP.
- final int STATE_BEFORE_KEYCAP = 1;
+ final int STATE_BEFORE_KEYCAP = 2;
// The offset is immediately before a variation selector and a KEYCAP.
- final int STATE_BEFORE_VS_AND_KEYCAP = 2;
+ final int STATE_BEFORE_VS_AND_KEYCAP = 3;
// The offset is immediately before an emoji modifier.
- final int STATE_BEFORE_EMOJI_MODIFIER = 3;
+ final int STATE_BEFORE_EMOJI_MODIFIER = 4;
// The offset is immediately before a variation selector and an emoji modifier.
- final int STATE_BEFORE_VS_AND_EMOJI_MODIFIER = 4;
+ final int STATE_BEFORE_VS_AND_EMOJI_MODIFIER = 5;
// The offset is immediately before a variation selector.
- final int STATE_BEFORE_VS = 5;
+ final int STATE_BEFORE_VS = 6;
// The offset is immediately before a ZWJ emoji.
- final int STATE_BEFORE_ZWJ_EMOJI = 6;
+ final int STATE_BEFORE_ZWJ_EMOJI = 7;
// The offset is immediately before a ZWJ that were seen before a ZWJ emoji.
- final int STATE_BEFORE_ZWJ = 7;
+ final int STATE_BEFORE_ZWJ = 8;
// The offset is immediately before a variation selector and a ZWJ that were seen before a
// ZWJ emoji.
- final int STATE_BEFORE_VS_AND_ZWJ = 8;
+ final int STATE_BEFORE_VS_AND_ZWJ = 9;
// The number of following RIS code points is odd.
- final int STATE_ODD_NUMBERED_RIS = 9;
+ final int STATE_ODD_NUMBERED_RIS = 10;
// The number of following RIS code points is even.
- final int STATE_EVEN_NUMBERED_RIS = 10;
+ final int STATE_EVEN_NUMBERED_RIS = 11;
// The state machine has been stopped.
- final int STATE_FINISHED = 11;
+ final int STATE_FINISHED = 12;
int deleteCharCount = 0; // Char count to be deleted by backspace.
int lastSeenVSCharCount = 0; // Char count of previous variation selector.
@@ -152,7 +158,9 @@
switch (state) {
case STATE_START:
deleteCharCount = Character.charCount(codePoint);
- if (isVariationSelector(codePoint)) {
+ if (codePoint == LINE_FEED) {
+ state = STATE_LF;
+ } else if (isVariationSelector(codePoint)) {
state = STATE_BEFORE_VS;
} else if (Emoji.isZwjEmoji(codePoint)) {
state = STATE_BEFORE_ZWJ_EMOJI;
@@ -166,6 +174,11 @@
state = STATE_FINISHED;
}
break;
+ case STATE_LF:
+ if (codePoint == CARRIAGE_RETURN) {
+ ++deleteCharCount;
+ }
+ state = STATE_FINISHED;
case STATE_ODD_NUMBERED_RIS:
if (Emoji.isRegionalIndicatorSymbol(codePoint)) {
deleteCharCount += 2; /* Char count of RIS */
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index 9ed4850..0a452db 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -251,9 +251,9 @@
+ "|[1-9][0-9]|[0-9]))");
/**
- * Valid UCS characters defined in RFC 3987.
+ * Valid UCS characters defined in RFC 3987. Excludes space characters.
*/
- private static final String UCS_CHAR =
+ private static final String UCS_CHAR = "[" +
"\u00A0-\uD7FF" +
"\uF900-\uFDCF" +
"\uFDF0-\uFFEF" +
@@ -270,7 +270,8 @@
"\uDA80\uDC00-\uDABF\uDFFD" +
"\uDAC0\uDC00-\uDAFF\uDFFD" +
"\uDB00\uDC00-\uDB3F\uDFFD" +
- "\uDB44\uDC00-\uDB7F\uDFFD";
+ "\uDB44\uDC00-\uDB7F\uDFFD" +
+ "&&[^\u00A0[\u2000-\u200A]\u2028\u2029\u202F\u3000]]";
/**
* Valid characters for IRI label defined in RFC 3987.
diff --git a/core/java/android/view/IDockedStackListener.aidl b/core/java/android/view/IDockedStackListener.aidl
index cbc8dbd..88ac271 100644
--- a/core/java/android/view/IDockedStackListener.aidl
+++ b/core/java/android/view/IDockedStackListener.aidl
@@ -42,4 +42,9 @@
* @param animDuration The duration of the animation for changing the minimized state.
*/
void onDockedStackMinimizedChanged(boolean minimized, long animDuration);
+
+ /**
+ * Called when window manager repositioned the docked stack after a screen rotation change.
+ */
+ void onDockSideChanged(int newDockSide);
}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index f44d4c1a..c4ed94f 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -485,25 +485,15 @@
}
/**
- * Halts any current rendering into the surface. Use this if it is unclear whether
+ * Stops any rendering into the surface. Use this if it is unclear whether
* or not the surface used by the HardwareRenderer will be changing. It
- * Suspends any rendering into the surface, but will not do any destruction.
- *
- * Any subsequent draws will override the pause, resuming normal operation.
+ * Suspends any rendering into the surface, but will not do any destruction
*/
boolean pauseSurface(Surface surface) {
return nPauseSurface(mNativeProxy, surface);
}
/**
- * Hard stops or resumes rendering into the surface. This flag is used to
- * determine whether or not it is safe to use the given surface *at all*
- */
- void setStopped(boolean stopped) {
- nSetStopped(mNativeProxy, stopped);
- }
-
- /**
* Destroys all hardware rendering resources associated with the specified
* view hierarchy.
*
@@ -903,6 +893,10 @@
nSerializeDisplayListTree(mNativeProxy);
}
+ public static boolean copySurfaceInto(Surface surface, Bitmap bitmap) {
+ return nCopySurfaceInto(surface, bitmap);
+ }
+
@Override
protected void finalize() throws Throwable {
try {
@@ -998,7 +992,6 @@
private static native void nInitialize(long nativeProxy, Surface window);
private static native void nUpdateSurface(long nativeProxy, Surface window);
private static native boolean nPauseSurface(long nativeProxy, Surface window);
- private static native void nSetStopped(long nativeProxy, boolean stopped);
private static native void nSetup(long nativeProxy, int width, int height,
float lightRadius, int ambientShadowAlpha, int spotShadowAlpha);
private static native void nSetLightCenter(long nativeProxy,
@@ -1040,4 +1033,6 @@
private static native long nAddFrameMetricsObserver(long nativeProxy, FrameMetricsObserver observer);
private static native void nRemoveFrameMetricsObserver(long nativeProxy, long nativeObserver);
+
+ private static native boolean nCopySurfaceInto(Surface surface, Bitmap bitmap);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7e51096..307e700 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10278,7 +10278,8 @@
*/
@Visibility boolean dispatchVisibilityAggregated(boolean isVisible) {
final boolean thisVisible = getVisibility() == VISIBLE;
- if (thisVisible) {
+ // If we're not visible but something is telling us we are, ignore it.
+ if (thisVisible || !isVisible) {
onVisibilityAggregated(isVisible);
}
return thisVisible && isVisible;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 94c4cef..a324767 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1079,16 +1079,13 @@
void setWindowStopped(boolean stopped) {
if (mStopped != stopped) {
mStopped = stopped;
- final ThreadedRenderer renderer = mAttachInfo.mHardwareRenderer;
- if (renderer != null) {
- if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped);
- renderer.setStopped(mStopped);
- }
if (!mStopped) {
scheduleTraversals();
} else {
- if (renderer != null) {
- renderer.destroyHardwareResources(mView);
+ if (mAttachInfo.mHardwareRenderer != null) {
+ if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle());
+ mAttachInfo.mHardwareRenderer.updateSurface(null);
+ mAttachInfo.mHardwareRenderer.destroyHardwareResources(mView);
}
}
}
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java
index fd73432..24739bf 100644
--- a/core/java/android/view/inputmethod/CursorAnchorInfo.java
+++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java
@@ -16,6 +16,7 @@
package android.view.inputmethod;
+import android.annotation.NonNull;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.os.Parcel;
@@ -25,6 +26,7 @@
import android.text.TextUtils;
import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -36,6 +38,11 @@
*/
public final class CursorAnchorInfo implements Parcelable {
/**
+ * The pre-computed hash code.
+ */
+ private final int mHashCode;
+
+ /**
* The index of the first character of the selected text (inclusive). {@code -1} when there is
* no text selection.
*/
@@ -100,7 +107,8 @@
* Transformation matrix that is applied to any positional information of this class to
* transform local coordinates into screen coordinates.
*/
- private final Matrix mMatrix;
+ @NonNull
+ private final float[] mMatrixValues;
/**
* Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the
@@ -121,6 +129,7 @@
public static final int FLAG_IS_RTL = 0x04;
public CursorAnchorInfo(final Parcel source) {
+ mHashCode = source.readInt();
mSelectionStart = source.readInt();
mSelectionEnd = source.readInt();
mComposingTextStart = source.readInt();
@@ -131,8 +140,7 @@
mInsertionMarkerBaseline = source.readFloat();
mInsertionMarkerBottom = source.readFloat();
mCharacterBoundsArray = source.readParcelable(SparseRectFArray.class.getClassLoader());
- mMatrix = new Matrix();
- mMatrix.setValues(source.createFloatArray());
+ mMatrixValues = source.createFloatArray();
}
/**
@@ -143,6 +151,7 @@
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mHashCode);
dest.writeInt(mSelectionStart);
dest.writeInt(mSelectionEnd);
dest.writeInt(mComposingTextStart);
@@ -153,27 +162,12 @@
dest.writeFloat(mInsertionMarkerBaseline);
dest.writeFloat(mInsertionMarkerBottom);
dest.writeParcelable(mCharacterBoundsArray, flags);
- final float[] matrixArray = new float[9];
- mMatrix.getValues(matrixArray);
- dest.writeFloatArray(matrixArray);
+ dest.writeFloatArray(mMatrixValues);
}
@Override
public int hashCode(){
- final float floatHash = mInsertionMarkerHorizontal + mInsertionMarkerTop
- + mInsertionMarkerBaseline + mInsertionMarkerBottom;
- int hash = floatHash > 0 ? (int) floatHash : (int)(-floatHash);
- hash *= 31;
- hash += mInsertionMarkerFlags;
- hash *= 31;
- hash += mSelectionStart + mSelectionEnd + mComposingTextStart;
- hash *= 31;
- hash += Objects.hashCode(mComposingText);
- hash *= 31;
- hash += Objects.hashCode(mCharacterBoundsArray);
- hash *= 31;
- hash += Objects.hashCode(mMatrix);
- return hash;
+ return mHashCode;
}
/**
@@ -202,13 +196,13 @@
if (hashCode() != that.hashCode()) {
return false;
}
+
+ // Check fields that are not covered by hashCode() first.
+
if (mSelectionStart != that.mSelectionStart || mSelectionEnd != that.mSelectionEnd) {
return false;
}
- if (mComposingTextStart != that.mComposingTextStart
- || !Objects.equals(mComposingText, that.mComposingText)) {
- return false;
- }
+
if (mInsertionMarkerFlags != that.mInsertionMarkerFlags
|| !areSameFloatImpl(mInsertionMarkerHorizontal, that.mInsertionMarkerHorizontal)
|| !areSameFloatImpl(mInsertionMarkerTop, that.mInsertionMarkerTop)
@@ -216,18 +210,35 @@
|| !areSameFloatImpl(mInsertionMarkerBottom, that.mInsertionMarkerBottom)) {
return false;
}
+
if (!Objects.equals(mCharacterBoundsArray, that.mCharacterBoundsArray)) {
return false;
}
- if (!Objects.equals(mMatrix, that.mMatrix)) {
+
+ // Following fields are (partially) covered by hashCode().
+
+ if (mComposingTextStart != that.mComposingTextStart
+ || !Objects.equals(mComposingText, that.mComposingText)) {
return false;
}
+
+ // We do not use Arrays.equals(float[], float[]) to keep the previous behavior regarding
+ // NaN, 0.0f, and -0.0f.
+ if (mMatrixValues.length != that.mMatrixValues.length) {
+ return false;
+ }
+ for (int i = 0; i < mMatrixValues.length; ++i) {
+ if (mMatrixValues[i] != that.mMatrixValues[i]) {
+ return false;
+ }
+ }
return true;
}
@Override
public String toString() {
- return "SelectionInfo{mSelection=" + mSelectionStart + "," + mSelectionEnd
+ return "CursorAnchorInfo{mHashCode=" + mHashCode
+ + " mSelection=" + mSelectionStart + "," + mSelectionEnd
+ " mComposingTextStart=" + mComposingTextStart
+ " mComposingText=" + Objects.toString(mComposingText)
+ " mInsertionMarkerFlags=" + mInsertionMarkerFlags
@@ -236,7 +247,7 @@
+ " mInsertionMarkerBaseline=" + mInsertionMarkerBaseline
+ " mInsertionMarkerBottom=" + mInsertionMarkerBottom
+ " mCharacterBoundsArray=" + Objects.toString(mCharacterBoundsArray)
- + " mMatrix=" + Objects.toString(mMatrix)
+ + " mMatrix=" + Arrays.toString(mMatrixValues)
+ "}";
}
@@ -254,7 +265,7 @@
private float mInsertionMarkerBottom = Float.NaN;
private int mInsertionMarkerFlags = 0;
private SparseRectFArrayBuilder mCharacterBoundsArrayBuilder = null;
- private final Matrix mMatrix = new Matrix(Matrix.IDENTITY_MATRIX);
+ private float[] mMatrixValues = null;
private boolean mMatrixInitialized = false;
/**
@@ -349,7 +360,10 @@
* is interpreted as an identity matrix.
*/
public Builder setMatrix(final Matrix matrix) {
- mMatrix.set(matrix != null ? matrix : Matrix.IDENTITY_MATRIX);
+ if (mMatrixValues == null) {
+ mMatrixValues = new float[9];
+ }
+ (matrix != null ? matrix : Matrix.IDENTITY_MATRIX).getValues(mMatrixValues);
mMatrixInitialized = true;
return this;
}
@@ -391,7 +405,6 @@
mInsertionMarkerTop = Float.NaN;
mInsertionMarkerBaseline = Float.NaN;
mInsertionMarkerBottom = Float.NaN;
- mMatrix.set(Matrix.IDENTITY_MATRIX);
mMatrixInitialized = false;
if (mCharacterBoundsArrayBuilder != null) {
mCharacterBoundsArrayBuilder.reset();
@@ -411,7 +424,18 @@
mInsertionMarkerBottom = builder.mInsertionMarkerBottom;
mCharacterBoundsArray = builder.mCharacterBoundsArrayBuilder != null ?
builder.mCharacterBoundsArrayBuilder.build() : null;
- mMatrix = new Matrix(builder.mMatrix);
+ mMatrixValues = new float[9];
+ if (builder.mMatrixInitialized) {
+ System.arraycopy(builder.mMatrixValues, 0, mMatrixValues, 0, 9);
+ } else {
+ Matrix.IDENTITY_MATRIX.getValues(mMatrixValues);
+ }
+
+ // To keep hash function simple, we only use some complex objects for hash.
+ int hash = Objects.hashCode(mComposingText);
+ hash *= 31;
+ hash += Arrays.hashCode(mMatrixValues);
+ mHashCode = hash;
}
/**
@@ -527,7 +551,9 @@
* @return a new instance (copy) of the transformation matrix.
*/
public Matrix getMatrix() {
- return new Matrix(mMatrix);
+ final Matrix matrix = new Matrix();
+ matrix.setValues(mMatrixValues);
+ return matrix;
}
/**
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index a42f4d9..dc433b1 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -68,6 +68,7 @@
// TODO: remove this
private static final String EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME =
"UntranslatableReplacementStringInSubtypeName";
+ private static final int SUBTYPE_ID_NONE = 0;
private final boolean mIsAuxiliary;
private final boolean mOverridesImplicitlyEnabledSubtype;
@@ -157,13 +158,13 @@
* track of enabled subtypes by ID. When the IME package gets upgraded, enabled IDs will
* stay enabled even if other attributes are different. If the ID is unspecified or 0,
* Arrays.hashCode(new Object[] {locale, mode, extraValue,
- * isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead.
+ * isAuxiliary, overridesImplicitlyEnabledSubtype, isAsciiCapable}) will be used instead.
*/
public InputMethodSubtypeBuilder setSubtypeId(int subtypeId) {
mSubtypeId = subtypeId;
return this;
}
- private int mSubtypeId = 0;
+ private int mSubtypeId = SUBTYPE_ID_NONE;
/**
* @param subtypeLocale is the locale supported by this subtype.
@@ -268,7 +269,7 @@
* subtypes by ID. When the IME package gets upgraded, enabled IDs will stay enabled even if
* other attributes are different. If the ID is unspecified or 0,
* Arrays.hashCode(new Object[] {locale, mode, extraValue,
- * isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead.
+ * isAuxiliary, overridesImplicitlyEnabledSubtype, isAsciiCapable}) will be used instead.
*/
public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, int id) {
@@ -293,9 +294,12 @@
mIsAsciiCapable = builder.mIsAsciiCapable;
// If hashCode() of this subtype is 0 and you want to specify it as an id of this subtype,
// just specify 0 as this subtype's id. Then, this subtype's id is treated as 0.
- mSubtypeHashCode = mSubtypeId != 0 ? mSubtypeId : hashCodeInternal(mSubtypeLocale,
- mSubtypeMode, mSubtypeExtraValue, mIsAuxiliary, mOverridesImplicitlyEnabledSubtype,
- mIsAsciiCapable);
+ if (mSubtypeId != SUBTYPE_ID_NONE) {
+ mSubtypeHashCode = mSubtypeId;
+ } else {
+ mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue,
+ mIsAuxiliary, mOverridesImplicitlyEnabledSubtype, mIsAsciiCapable);
+ }
}
InputMethodSubtype(Parcel source) {
@@ -501,6 +505,22 @@
return mSubtypeHashCode;
}
+ /**
+ * @hide
+ * @return {@code true} if a valid subtype ID exists.
+ */
+ public final boolean hasSubtypeId() {
+ return mSubtypeId != SUBTYPE_ID_NONE;
+ }
+
+ /**
+ * @hide
+ * @return subtype ID. {@code 0} means that not subtype ID is specified.
+ */
+ public final int getSubtypeId() {
+ return mSubtypeId;
+ }
+
@Override
public boolean equals(Object o) {
if (o instanceof InputMethodSubtype) {
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 7220256..24d2c8e 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -58,8 +58,8 @@
private static final String TAG = "RadialTimePickerView";
- private static final int HOURS = 0;
- private static final int MINUTES = 1;
+ public static final int HOURS = 0;
+ public static final int MINUTES = 1;
private static final int HOURS_INNER = 2;
private static final int SELECTOR_CIRCLE = 0;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 6d2cea6..a9b7f4e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1856,6 +1856,7 @@
public static final int LAYOUT_MARGIN_END = 1;
/** Set width */
public static final int LAYOUT_WIDTH = 2;
+ public static final int LAYOUT_MARGIN_BOTTOM = 3;
/**
* @param viewId ID of the view alter
@@ -1898,6 +1899,12 @@
target.setLayoutParams(layoutParams);
}
break;
+ case LAYOUT_MARGIN_BOTTOM:
+ if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
+ ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = value;
+ target.setLayoutParams(layoutParams);
+ }
+ break;
case LAYOUT_WIDTH:
layoutParams.width = value;
target.setLayoutParams(layoutParams);
@@ -2870,6 +2877,16 @@
}
/**
+ * Equivalent to setting {@link android.view.ViewGroup.MarginLayoutParams#bottomMargin}.
+ *
+ * @hide
+ */
+ public void setViewLayoutMarginBottom(int viewId, int bottomMargin) {
+ addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_BOTTOM,
+ bottomMargin));
+ }
+
+ /**
* Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width}.
* @hide
*/
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index ff10287..a585d75 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -555,7 +555,15 @@
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- getContext().registerReceiver(mIntentReceiver, filter, null, getHandler());
+ // OK, this is gross but needed. This class is supported by the
+ // remote views mechanism and as a part of that the remote views
+ // can be inflated by a context for another user without the app
+ // having interact users permission - just for loading resources.
+ // For example, when adding widgets from a managed profile to the
+ // home screen. Therefore, we register the receiver as the user
+ // the app is running as not the one the context is for.
+ getContext().registerReceiverAsUser(mIntentReceiver, android.os.Process.myUserHandle(),
+ filter, null, getHandler());
}
private void registerObserver() {
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 4a24e26..0c3892d 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -58,8 +58,8 @@
private static final long DELAY_COMMIT_MILLIS = 2000;
// Index used by RadialPickerLayout
- private static final int HOUR_INDEX = 0;
- private static final int MINUTE_INDEX = 1;
+ private static final int HOUR_INDEX = RadialTimePickerView.HOURS;
+ private static final int MINUTE_INDEX = RadialTimePickerView.MINUTES;
// NOT a real index for the purpose of what's showing.
private static final int AMPM_INDEX = 2;
@@ -82,6 +82,10 @@
private final Calendar mTempCalendar;
+ // Accessibility strings.
+ private final String mSelectHours;
+ private final String mSelectMinutes;
+
private boolean mIsEnabled = true;
private boolean mAllowAutoAdvance;
private int mInitialHourOfDay;
@@ -89,10 +93,6 @@
private boolean mIs24Hour;
private boolean mIsAmPmAtStart;
- // Accessibility strings.
- private String mSelectHours;
- private String mSelectMinutes;
-
// Localization data.
private boolean mHourFormatShowLeadingZero;
private boolean mHourFormatStartsAtZero;
@@ -520,11 +520,15 @@
} else {
flags |= DateUtils.FORMAT_12HOUR;
}
+
mTempCalendar.set(Calendar.HOUR_OF_DAY, getHour());
mTempCalendar.set(Calendar.MINUTE, getMinute());
- String selectedDate = DateUtils.formatDateTime(mContext,
+
+ final String selectedTime = DateUtils.formatDateTime(mContext,
mTempCalendar.getTimeInMillis(), flags);
- event.getText().add(selectedDate);
+ final String selectionMode = mRadialTimePickerView.getCurrentItemShowing() == HOUR_INDEX ?
+ mSelectHours : mSelectMinutes;
+ event.getText().add(selectedTime + " " + selectionMode);
}
/**
diff --git a/core/java/com/android/internal/app/LocaleHelper.java b/core/java/com/android/internal/app/LocaleHelper.java
index a9d5113..7e9587a 100644
--- a/core/java/com/android/internal/app/LocaleHelper.java
+++ b/core/java/com/android/internal/app/LocaleHelper.java
@@ -90,6 +90,15 @@
return str.toUpperCase();
}
+ // For some locales we want to use a "dialect" form, for instance
+ // "Dari" instead of "Persian (Afghanistan)", or "Moldavian" instead of "Romanian (Moldova)"
+ private static boolean shouldUseDialectName(Locale locale) {
+ final String lang = locale.getLanguage();
+ return "fa".equals(lang) // Persian
+ || "ro".equals(lang) // Romanian
+ || "zh".equals(lang); // Chinese
+ }
+
/**
* Returns the locale localized for display in the provided locale.
*
@@ -99,8 +108,10 @@
* @return the localized name of the locale.
*/
public static String getDisplayName(Locale locale, Locale displayLocale, boolean sentenceCase) {
- String result = ULocale.getDisplayNameWithDialect(locale.toLanguageTag(),
- ULocale.forLocale(displayLocale));
+ final ULocale displayULocale = ULocale.forLocale(displayLocale);
+ String result = shouldUseDialectName(locale)
+ ? ULocale.getDisplayNameWithDialect(locale.toLanguageTag(), displayULocale)
+ : ULocale.getDisplayName(locale.toLanguageTag(), displayULocale);
return sentenceCase ? toSentenceCase(result, displayLocale) : result;
}
@@ -112,9 +123,7 @@
* @return the localized name of the locale.
*/
public static String getDisplayName(Locale locale, boolean sentenceCase) {
- String result = ULocale.getDisplayNameWithDialect(locale.toLanguageTag(),
- ULocale.getDefault());
- return sentenceCase ? toSentenceCase(result, Locale.getDefault()) : result;
+ return getDisplayName(locale, Locale.getDefault(), sentenceCase);
}
/**
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index f2bf9e1..3fc02a7 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -16,7 +16,9 @@
package com.android.internal.app;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.app.Activity;
import android.app.ActivityThread;
import android.app.VoiceInteractor.PickOptionRequest;
@@ -24,6 +26,7 @@
import android.app.VoiceInteractor.Prompt;
import android.content.pm.ComponentInfo;
import android.os.AsyncTask;
+import android.provider.MediaStore;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Slog;
@@ -119,37 +122,62 @@
}
};
+ /**
+ * Get the string resource to be used as a label for the link to the resolver activity for an
+ * action.
+ *
+ * @param action The action to resolve
+ *
+ * @return The string resource to be used as a label
+ */
+ public static @StringRes int getLabelRes(String action) {
+ return ActionTitle.forAction(action).labelRes;
+ }
+
private enum ActionTitle {
VIEW(Intent.ACTION_VIEW,
com.android.internal.R.string.whichViewApplication,
- com.android.internal.R.string.whichViewApplicationNamed),
+ com.android.internal.R.string.whichViewApplicationNamed,
+ com.android.internal.R.string.whichViewApplicationLabel),
EDIT(Intent.ACTION_EDIT,
com.android.internal.R.string.whichEditApplication,
- com.android.internal.R.string.whichEditApplicationNamed),
+ com.android.internal.R.string.whichEditApplicationNamed,
+ com.android.internal.R.string.whichEditApplicationLabel),
SEND(Intent.ACTION_SEND,
com.android.internal.R.string.whichSendApplication,
- com.android.internal.R.string.whichSendApplicationNamed),
+ com.android.internal.R.string.whichSendApplicationNamed,
+ com.android.internal.R.string.whichSendApplicationLabel),
SENDTO(Intent.ACTION_SENDTO,
com.android.internal.R.string.whichSendToApplication,
- com.android.internal.R.string.whichSendToApplicationNamed),
+ com.android.internal.R.string.whichSendToApplicationNamed,
+ com.android.internal.R.string.whichSendToApplicationLabel),
SEND_MULTIPLE(Intent.ACTION_SEND_MULTIPLE,
com.android.internal.R.string.whichSendApplication,
- com.android.internal.R.string.whichSendApplicationNamed),
+ com.android.internal.R.string.whichSendApplicationNamed,
+ com.android.internal.R.string.whichSendApplicationLabel),
+ CAPTURE_IMAGE(MediaStore.ACTION_IMAGE_CAPTURE,
+ com.android.internal.R.string.whichImageCaptureApplication,
+ com.android.internal.R.string.whichImageCaptureApplicationNamed,
+ com.android.internal.R.string.whichImageCaptureApplicationLabel),
DEFAULT(null,
com.android.internal.R.string.whichApplication,
- com.android.internal.R.string.whichApplicationNamed),
+ com.android.internal.R.string.whichApplicationNamed,
+ com.android.internal.R.string.whichApplicationLabel),
HOME(Intent.ACTION_MAIN,
com.android.internal.R.string.whichHomeApplication,
- com.android.internal.R.string.whichHomeApplicationNamed);
+ com.android.internal.R.string.whichHomeApplicationNamed,
+ com.android.internal.R.string.whichHomeApplicationLabel);
public final String action;
public final int titleRes;
public final int namedTitleRes;
+ public final @StringRes int labelRes;
- ActionTitle(String action, int titleRes, int namedTitleRes) {
+ ActionTitle(String action, int titleRes, int namedTitleRes, @StringRes int labelRes) {
this.action = action;
this.titleRes = titleRes;
this.namedTitleRes = namedTitleRes;
+ this.labelRes = labelRes;
}
public static ActionTitle forAction(String action) {
@@ -760,7 +788,7 @@
} else {
try {
AppGlobals.getPackageManager().setLastChosenActivity(intent,
- intent.resolveTypeIfNeeded(getContentResolver()),
+ intent.resolveType(getContentResolver()),
PackageManager.MATCH_DEFAULT_ONLY,
filter, bestMatch, intent.getComponent());
} catch (RemoteException re) {
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 06542f7..2f80b86 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -516,7 +516,7 @@
out.writeInt((int)val);
} else {
int top = ~((int)((val>>32)&0x7fffffff));
- int bottom = (int)(val&0xfffffff);
+ int bottom = (int)(val&0x0ffffffffL);
out.writeInt(top);
out.writeInt(bottom);
}
diff --git a/core/java/com/android/internal/app/procstats/SparseMappingTable.java b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
index 76102af..f941836 100644
--- a/core/java/com/android/internal/app/procstats/SparseMappingTable.java
+++ b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
@@ -68,11 +68,8 @@
* A table of data as stored in a SparseMappingTable.
*/
public static class Table {
- // When mSequence is this this our data better be empty
- private static final int UNINITIALIZED_SEQUENCE = -1;
-
private SparseMappingTable mParent;
- private int mSequence = UNINITIALIZED_SEQUENCE;
+ private int mSequence = 1;
private int[] mTable;
private int mSize;
@@ -119,12 +116,6 @@
* but should be considered opaque to the caller.
*/
public int getOrAddKey(byte id, int count) {
- // This is the only place we add data to mParent.mLongs, so this is the time
- // to update our sequence to match there.
- if (mSequence == UNINITIALIZED_SEQUENCE) {
- mSequence = mParent.mSequence;
- }
-
assertConsistency();
final int idx = binarySearch(id);
@@ -311,7 +302,7 @@
// Reset our sequence number. This will make all read/write calls
// start to fail, and then when we re-allocate it will be re-synced
// to that of mParent.
- mSequence = UNINITIALIZED_SEQUENCE;
+ mSequence = mParent.mSequence;
}
/**
@@ -377,27 +368,19 @@
// Original bug: b/27045736
// New bug: b/27960286
if (false) {
- // Assert that our sequence number has been initialized. If it hasn't
- // that means someone tried to read or write data without allocating it
- // since we were created or reset.
- if (mSequence == UNINITIALIZED_SEQUENCE) {
- logOrThrow("mSequence == UNINITIALIZED_SEQUENCE in"
- + " SparseMappingTable.Table. -- "
- + dumpInternalState());
- return;
- }
-
// Assert that our sequence number matches mParent's. If it isn't that means
- // we have been reset and our
+ // we have been reset and our. If our sequence is UNITIALIZED_SEQUENCE, then
+ // it's possible that everything is working fine and we just haven't been
+ // written to since the last resetTable().
if (mSequence != mParent.mSequence) {
if (mSequence < mParent.mSequence) {
- logOrThrow("Sequence mismatch. SparseMappingTable.resetTable()"
+ logOrThrow("Sequence mismatch. SparseMappingTable.reset()"
+ " called but not Table.resetTable() -- "
+ dumpInternalState());
return;
} else if (mSequence > mParent.mSequence) {
logOrThrow("Sequence mismatch. Table.resetTable()"
- + " called but not SparseMappingTable.resetTable() -- "
+ + " called but not SparseMappingTable.reset() -- "
+ dumpInternalState());
return;
}
@@ -494,6 +477,10 @@
}
}
+ public SparseMappingTable() {
+ mLongs.add(new long[ARRAY_SIZE]);
+ }
+
/**
* Wipe out all the data.
*/
@@ -545,6 +532,35 @@
}
/**
+ * Return a string for debugging.
+ */
+ public String dumpInternalState(boolean includeData) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("SparseMappingTable{");
+ sb.append("mSequence=");
+ sb.append(mSequence);
+ sb.append(" mNextIndex=");
+ sb.append(mNextIndex);
+ sb.append(" mLongs.size=");
+ final int N = mLongs.size();
+ sb.append(N);
+ sb.append("\n");
+ if (includeData) {
+ for (int i=0; i<N; i++) {
+ final long[] array = mLongs.get(i);
+ for (int j=0; j<array.length; j++) {
+ if (i == N-1 && j == mNextIndex) {
+ break;
+ }
+ sb.append(String.format(" %4d %d 0x%016x %-19d\n", i, j, array[j], array[j]));
+ }
+ }
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
* Write the long array to the parcel in a compacted form. Does not allow negative
* values in the array.
*/
@@ -559,7 +575,7 @@
out.writeInt((int)val);
} else {
int top = ~((int)((val>>32)&0x7fffffff));
- int bottom = (int)(val&0xfffffff);
+ int bottom = (int)(val&0x0ffffffffL);
out.writeInt(top);
out.writeInt(bottom);
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index 46b49de..8d11783 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -16,6 +16,7 @@
package com.android.internal.inputmethod;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.text.TextUtils;
@@ -284,8 +285,22 @@
return -1;
}
+ /**
+ * Provides the basic operation to implement bi-directional IME rotation.
+ * @param onlyCurrentIme {@code true} to limit the search space to IME subtypes that belong
+ * to {@code imi}.
+ * @param imi {@link InputMethodInfo} that will be used in conjunction with {@code subtype}
+ * from which we find the adjacent IME subtype.
+ * @param subtype {@link InputMethodSubtype} that will be used in conjunction with
+ * {@code imi} from which we find the next IME subtype. {@code null} if the input method
+ * does not have a subtype.
+ * @param forward {@code true} to do forward search the next IME subtype. Specify
+ * {@code false} to do backward search.
+ * @return The IME subtype found. {@code null} if no IME subtype is found.
+ */
+ @Nullable
public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
- InputMethodInfo imi, InputMethodSubtype subtype, boolean forward) {
+ InputMethodInfo imi, @Nullable InputMethodSubtype subtype, boolean forward) {
if (imi == null) {
return null;
}
@@ -298,7 +313,7 @@
}
final int N = mImeSubtypeList.size();
for (int i = 1; i < N; ++i) {
- // Start searching the next IME/subtype from the next of the current index.
+ // Start searching the next IME/subtype from +/- 1 indices.
final int offset = forward ? i : N - i;
final int candidateIndex = (currentIndex + offset) % N;
final ImeSubtypeListItem candidate = mImeSubtypeList.get(candidateIndex);
@@ -371,8 +386,22 @@
mUsageHistoryOfSubtypeListItemIndex[0] = currentItemIndex;
}
+ /**
+ * Provides the basic operation to implement bi-directional IME rotation.
+ * @param onlyCurrentIme {@code true} to limit the search space to IME subtypes that belong
+ * to {@code imi}.
+ * @param imi {@link InputMethodInfo} that will be used in conjunction with {@code subtype}
+ * from which we find the adjacent IME subtype.
+ * @param subtype {@link InputMethodSubtype} that will be used in conjunction with
+ * {@code imi} from which we find the next IME subtype. {@code null} if the input method
+ * does not have a subtype.
+ * @param forward {@code true} to do forward search the next IME subtype. Specify
+ * {@code false} to do backward search.
+ * @return The IME subtype found. {@code null} if no IME subtype is found.
+ */
+ @Nullable
public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
- InputMethodInfo imi, InputMethodSubtype subtype, boolean forward) {
+ InputMethodInfo imi, @Nullable InputMethodSubtype subtype, boolean forward) {
int currentUsageRank = getUsageRank(imi, subtype);
if (currentUsageRank < 0) {
if (DEBUG) {
@@ -456,8 +485,22 @@
mSwitchingUnawareRotationList = switchingUnawareRotationList;
}
+ /**
+ * Provides the basic operation to implement bi-directional IME rotation.
+ * @param onlyCurrentIme {@code true} to limit the search space to IME subtypes that belong
+ * to {@code imi}.
+ * @param imi {@link InputMethodInfo} that will be used in conjunction with {@code subtype}
+ * from which we find the adjacent IME subtype.
+ * @param subtype {@link InputMethodSubtype} that will be used in conjunction with
+ * {@code imi} from which we find the next IME subtype. {@code null} if the input method
+ * does not have a subtype.
+ * @param forward {@code true} to do forward search the next IME subtype. Specify
+ * {@code false} to do backward search.
+ * @return The IME subtype found. {@code null} if no IME subtype is found.
+ */
+ @Nullable
public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme, InputMethodInfo imi,
- InputMethodSubtype subtype, boolean forward) {
+ @Nullable InputMethodSubtype subtype, boolean forward) {
if (imi == null) {
return null;
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 5980ab6..78b5d61 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -43,6 +43,7 @@
import dalvik.system.DexFile;
import dalvik.system.PathClassLoader;
import dalvik.system.VMRuntime;
+import dalvik.system.ZygoteHooks;
import libcore.io.IoUtils;
@@ -597,6 +598,10 @@
}
public static void main(String argv[]) {
+ // Mark zygote start. This ensures that thread creation will throw
+ // an error.
+ ZygoteHooks.startZygoteNoThreadCreation();
+
try {
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygoteInit");
RuntimeInit.enableDdms();
@@ -648,6 +653,8 @@
// Zygote process unmounts root storage spaces.
Zygote.nativeUnmountStorageOnInit();
+ ZygoteHooks.stopZygoteNoThreadCreation();
+
if (startSystemServer) {
startSystemServer(abiList, socketName);
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index f9ac563..3aa7719 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -210,7 +210,6 @@
private Drawable mResizingBackgroundDrawable;
private Drawable mCaptionBackgroundDrawable;
private Drawable mUserCaptionBackgroundDrawable;
- private Drawable mOriginalBackgroundDrawable;
private float mAvailableWidth;
@@ -891,11 +890,6 @@
mBackgroundPadding.setEmpty();
}
drawableChanged();
-
- // Make sure we don't reset to the old drawable when finishing resizing.
- if (mResizeMode != RESIZE_MODE_INVALID) {
- mOriginalBackgroundDrawable = null;
- }
}
}
@@ -1960,9 +1954,6 @@
updateElevation();
updateColorViews(null /* insets */, false);
-
- mOriginalBackgroundDrawable = getBackground();
- setBackgroundDrawable(null);
}
mResizeMode = resizeMode;
getViewRootImpl().requestInvalidateRootRenderNode();
@@ -1974,10 +1965,6 @@
updateColorViews(null /* insets */, false);
mResizeMode = RESIZE_MODE_INVALID;
getViewRootImpl().requestInvalidateRootRenderNode();
- if (mOriginalBackgroundDrawable != null) {
- setBackgroundDrawable(mOriginalBackgroundDrawable);
- mOriginalBackgroundDrawable = null;
- }
}
@Override
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 5992f7a..b075db8 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -57,7 +57,7 @@
public static final int BASE_DATA_CONNECTION = 0x00040000;
public static final int BASE_DATA_CONNECTION_AC = 0x00041000;
public static final int BASE_DATA_CONNECTION_TRACKER = 0x00042000;
- public static final int BASE_DNS_PINGER = 0x00050000;
+ public static final int BASE_TETHERING = 0x00050000;
public static final int BASE_NSD_MANAGER = 0x00060000;
public static final int BASE_NETWORK_STATE_TRACKER = 0x00070000;
public static final int BASE_CONNECTIVITY_MANAGER = 0x00080000;
diff --git a/core/java/com/android/internal/widget/ImageFloatingTextView.java b/core/java/com/android/internal/widget/ImageFloatingTextView.java
index 78c5e34..e2d8ffc 100644
--- a/core/java/com/android/internal/widget/ImageFloatingTextView.java
+++ b/core/java/com/android/internal/widget/ImageFloatingTextView.java
@@ -16,13 +16,18 @@
package com.android.internal.widget;
+import com.android.internal.R;
+
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
import android.text.BoringLayout;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.TypedValue;
import android.view.RemotableViewMethod;
import android.widget.RemoteViews;
import android.widget.TextView;
@@ -35,7 +40,8 @@
@RemoteViews.RemoteView
public class ImageFloatingTextView extends TextView {
- private boolean mHasImage;
+ /** Number of lines from the top to indent */
+ private int mIndentLines;
public ImageFloatingTextView(Context context) {
this(context, null);
@@ -69,10 +75,16 @@
.setEllipsizedWidth(ellipsisWidth)
.setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
- // we set the endmargin on the first 2 lines. this works just in our case but that's
- // sufficient for now.
- int endMargin = (int) (getResources().getDisplayMetrics().density * 52);
- int[] margins = mHasImage ? new int[] {endMargin, endMargin, 0} : null;
+ // we set the endmargin on the requested number of lines.
+ int endMargin = getContext().getResources().getDimensionPixelSize(
+ R.dimen.notification_content_picture_margin);
+ int[] margins = null;
+ if (mIndentLines > 0) {
+ margins = new int[mIndentLines + 1];
+ for (int i = 0; i < mIndentLines; i++) {
+ margins[i] = endMargin;
+ }
+ }
if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
builder.setIndents(margins, null);
} else {
@@ -84,8 +96,22 @@
@RemotableViewMethod
public void setHasImage(boolean hasImage) {
- mHasImage = hasImage;
+ mIndentLines = hasImage ? 2 : 0;
// The new layout will be automatically created when the text is
// set again by the notification.
}
+
+ /**
+ * @param lines the number of lines at the top that should be indented by indentEnd
+ * @return whether a change was made
+ */
+ public boolean setNumIndentLines(int lines) {
+ if (mIndentLines != lines) {
+ mIndentLines = lines;
+ // Invalidate layout.
+ setHint(getHint());
+ return true;
+ }
+ return false;
+ }
}
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
new file mode 100644
index 0000000..dc7b7f5
--- /dev/null
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.widget;
+
+import com.android.internal.R;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.RemotableViewMethod;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RemoteViews;
+
+/**
+ * A custom-built layout for the Notification.MessagingStyle.
+ *
+ * Evicts children until they all fit.
+ */
+@RemoteViews.RemoteView
+public class MessagingLinearLayout extends ViewGroup {
+
+ /**
+ * Spacing to be applied between views.
+ */
+ private int mSpacing;
+
+ /**
+ * The maximum height allowed.
+ */
+ private int mMaxHeight;
+
+ private int mIndentLines;
+
+ public MessagingLinearLayout(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.MessagingLinearLayout, 0,
+ 0);
+
+ final int N = a.getIndexCount();
+ for (int i = 0; i < N; i++) {
+ int attr = a.getIndex(i);
+ switch (attr) {
+ case R.styleable.MessagingLinearLayout_maxHeight:
+ mMaxHeight = a.getDimensionPixelSize(i, 0);
+ break;
+ case R.styleable.MessagingLinearLayout_spacing:
+ mSpacing = a.getDimensionPixelSize(i, 0);
+ break;
+ }
+ }
+
+ if (mMaxHeight <= 0) {
+ throw new IllegalStateException(
+ "MessagingLinearLayout: Must specify positive maxHeight");
+ }
+
+ a.recycle();
+ }
+
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // This is essentially a bottom-up linear layout that only adds children that fit entirely
+ // up to a maximum height.
+
+ switch (MeasureSpec.getMode(heightMeasureSpec)) {
+ case MeasureSpec.AT_MOST:
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ Math.min(mMaxHeight, MeasureSpec.getSize(heightMeasureSpec)),
+ MeasureSpec.AT_MOST);
+ break;
+ case MeasureSpec.UNSPECIFIED:
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ mMaxHeight,
+ MeasureSpec.AT_MOST);
+ break;
+ case MeasureSpec.EXACTLY:
+ break;
+ }
+ final int targetHeight = MeasureSpec.getSize(heightMeasureSpec);
+ final int count = getChildCount();
+
+ for (int i = 0; i < count; ++i) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ lp.hide = true;
+ }
+
+ int totalHeight = mPaddingTop + mPaddingBottom;
+ boolean first = true;
+
+ // Starting from the bottom: we measure every view as if it were the only one. If it still
+ // fits, we take it, otherwise we stop there.
+ for (int i = count - 1; i >= 0 && totalHeight < targetHeight; i--) {
+ if (getChildAt(i).getVisibility() == GONE) {
+ continue;
+ }
+ final View child = getChildAt(i);
+ LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
+
+ if (child instanceof ImageFloatingTextView) {
+ // Pretend we need the image padding for all views, we don't know which
+ // one will end up needing to do this (might end up not using all the space,
+ // but calculating this exactly would be more expensive).
+ ((ImageFloatingTextView) child).setNumIndentLines(mIndentLines);
+ }
+
+ measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
+
+ final int childHeight = child.getMeasuredHeight();
+ int newHeight = Math.max(totalHeight, totalHeight + childHeight + lp.topMargin +
+ lp.bottomMargin + (first ? 0 : mSpacing));
+ first = false;
+
+ if (newHeight <= targetHeight) {
+ totalHeight = newHeight;
+ lp.hide = false;
+ } else {
+ break;
+ }
+ }
+
+ // Now that we know which views to take, fix up the indents and see what width we get.
+ int measuredWidth = mPaddingLeft + mPaddingRight;
+ int imageLines = mIndentLines;
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (child.getVisibility() == GONE || lp.hide) {
+ continue;
+ }
+
+ if (child instanceof ImageFloatingTextView) {
+ ImageFloatingTextView textChild = (ImageFloatingTextView) child;
+ if (imageLines == 2 && textChild.getLineCount() > 2) {
+ // HACK: If we need indent for two lines, and they're coming from the same
+ // view, we need extra spacing to compensate for the lack of margins,
+ // so add an extra line of indent.
+ imageLines = 3;
+ }
+ boolean changed = textChild.setNumIndentLines(Math.max(0, imageLines));
+ if (changed) {
+ measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
+ }
+ imageLines -= textChild.getLineCount();
+ }
+
+ measuredWidth = Math.max(measuredWidth,
+ child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin
+ + mPaddingLeft + mPaddingRight);
+ }
+
+
+ setMeasuredDimension(
+ resolveSize(Math.max(getSuggestedMinimumWidth(), measuredWidth),
+ widthMeasureSpec),
+ resolveSize(Math.max(getSuggestedMinimumHeight(), totalHeight),
+ heightMeasureSpec));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ final int paddingLeft = mPaddingLeft;
+
+ int childTop;
+
+ // Where right end of child should go
+ final int width = right - left;
+ final int childRight = width - mPaddingRight;
+
+ final int layoutDirection = getLayoutDirection();
+ final int count = getChildCount();
+
+ childTop = mPaddingTop;
+
+ boolean first = true;
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (child.getVisibility() == GONE || lp.hide) {
+ continue;
+ }
+
+ final int childWidth = child.getMeasuredWidth();
+ final int childHeight = child.getMeasuredHeight();
+
+ int childLeft;
+ if (layoutDirection == LAYOUT_DIRECTION_RTL) {
+ childLeft = childRight - childWidth - lp.rightMargin;
+ } else {
+ childLeft = paddingLeft + lp.leftMargin;
+ }
+
+ if (!first) {
+ childTop += mSpacing;
+ }
+
+ childTop += lp.topMargin;
+ child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
+
+ childTop += childHeight + lp.bottomMargin;
+
+ first = false;
+ }
+ }
+
+ @Override
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.hide) {
+ return true;
+ }
+ return super.drawChild(canvas, child, drawingTime);
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(mContext, attrs);
+ }
+
+ @Override
+ protected LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+
+ }
+
+ @Override
+ protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
+ LayoutParams copy = new LayoutParams(lp.width, lp.height);
+ if (lp instanceof MarginLayoutParams) {
+ copy.copyMarginsFrom((MarginLayoutParams) lp);
+ }
+ return copy;
+ }
+
+ @RemotableViewMethod
+ /**
+ * Sets how many lines should be indented to avoid a floating image.
+ */
+ public void setNumIndentLines(int numberLines) {
+ mIndentLines = numberLines;
+ }
+
+ public static class LayoutParams extends MarginLayoutParams {
+
+ boolean hide = false;
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 25b487e..c4347f8 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -17,14 +17,9 @@
package com.android.internal.widget;
-import android.annotation.NonNull;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -90,10 +85,6 @@
private final float mMinFlingVelocity;
private final OverScroller mScroller;
private final VelocityTracker mVelocityTracker;
- private final Drawable mScrollIndicatorDrawable;
- private final Drawable mFakeForeground;
-
- private View mButtonBar;
private OnDismissedListener mOnDismissedListener;
private RunOnDismissedListener mRunOnDismissedListener;
@@ -115,8 +106,6 @@
}
};
- private final int[] mTempOffset = new int[2];
-
public ResolverDrawerLayout(Context context) {
this(context, null);
}
@@ -138,9 +127,6 @@
mMaxCollapsedHeight);
a.recycle();
- mScrollIndicatorDrawable = mContext.getDrawable(R.drawable.scroll_indicator_material);
- mFakeForeground = new ColorDrawable(Color.TRANSPARENT);
-
mScroller = new OverScroller(context, AnimationUtils.loadInterpolator(context,
android.R.interpolator.decelerate_quint));
mVelocityTracker = VelocityTracker.obtain();
@@ -152,13 +138,6 @@
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mButtonBar = findViewById(R.id.button_bar);
- }
-
public void setSmallCollapsed(boolean smallCollapsed) {
mSmallCollapsed = smallCollapsed;
requestLayout();
@@ -223,7 +202,8 @@
}
final boolean isCollapsedNew = mCollapseOffset != 0;
if (isCollapsedOld != isCollapsedNew) {
- onCollapsedChanged(isCollapsedNew);
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
} else {
// Start out collapsed at first unless we restored state for otherwise
@@ -462,7 +442,8 @@
mTopOffset += dy;
final boolean isCollapsedNew = newPos != 0;
if (isCollapsedOld != isCollapsedNew) {
- onCollapsedChanged(isCollapsedNew);
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
postInvalidateOnAnimation();
return dy;
@@ -470,14 +451,6 @@
return 0;
}
- private void onCollapsedChanged(boolean isCollapsed) {
- notifyViewAccessibilityStateChangedIfNeeded(
- AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
-
- // Set a fake foreground so that we receive onDrawForeground().
- setForeground(isCollapsed ? mFakeForeground : null);
- }
-
void dispatchOnDismissed() {
if (mOnDismissedListener != null) {
mOnDismissedListener.onDismissed();
@@ -736,23 +709,6 @@
}
@Override
- public void onDrawForeground(Canvas canvas) {
- if (isCollapsed() && mButtonBar != null) {
- // Draw the scroll indicator directly above the button bar.
- final int height = mScrollIndicatorDrawable.getIntrinsicHeight();
- mButtonBar.getLocationInWindow(mTempOffset);
- final int barTop = mTempOffset[1];
- getLocationInWindow(mTempOffset);
- final int myTop = mTempOffset[1];
- final int top = (barTop - myTop) - height;
- mScrollIndicatorDrawable.setBounds(0, top, getWidth(), top + height);
- mScrollIndicatorDrawable.draw(canvas);
- }
-
- super.onDrawForeground(canvas);
- }
-
- @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int sourceWidth = MeasureSpec.getSize(widthMeasureSpec);
int widthSize = sourceWidth;
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index e5c4a2d..50d86ff 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -343,7 +343,7 @@
}
static const JNINativeMethod gMethods[] = {
- {"nCreateRenderer", "!(J)J", (void*)createTree},
+ {"nCreateTree", "!(J)J", (void*)createTree},
{"nSetRendererViewportSize", "!(JFF)V", (void*)setTreeViewportSize},
{"nSetRootAlpha", "!(JF)Z", (void*)setRootAlpha},
{"nGetRootAlpha", "!(J)F", (void*)getRootAlpha},
diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp
index 91a3b4f..5c961d9 100644
--- a/core/jni/android_hardware_location_ContextHubService.cpp
+++ b/core/jni/android_hardware_location_ContextHubService.cpp
@@ -21,8 +21,8 @@
#include <inttypes.h>
#include <jni.h>
-#include <map>
#include <queue>
+#include <unordered_map>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
@@ -105,7 +105,7 @@
context_hub_info_s hubInfo;
jniInfo_s jniInfo;
std::queue<int> freeIds;
- std::map<int, app_instance_info_s *> appInstances;
+ std::unordered_map<int, app_instance_info_s> appInstances;
};
} // unnamed namespace
@@ -137,7 +137,7 @@
const context_hub_t *info = get_hub_info(hubHandle);
if (info) {
- msg->app = info->os_app_name;
+ msg->app_name = info->os_app_name;
return 0;
} else {
ALOGD("%s: Hub information is null for hubHandle %d", __FUNCTION__, hubHandle);
@@ -154,23 +154,23 @@
}
static int get_hub_id_for_app_instance(int id) {
- if (db.appInstances.find(id) == db.appInstances.end()) {
+ if (!db.appInstances.count(id)) {
ALOGD("%s: Cannot find app for app instance %d", __FUNCTION__, id);
return -1;
}
- int hubHandle = db.appInstances[id]->hubHandle;
+ int hubHandle = db.appInstances[id].hubHandle;
return db.hubInfo.hubs[hubHandle].hub_id;
}
static int set_dest_app(hub_message_t *msg, int id) {
- if (db.appInstances.find(id) == db.appInstances.end()) {
+ if (!db.appInstances.count(id)) {
ALOGD("%s: Cannod find app for app instance %d", __FUNCTION__, id);
return -1;
}
- msg->app = db.appInstances[id]->appInfo.name;
+ msg->app_name = db.appInstances[id].appInfo.app_name;
return 0;
}
@@ -210,76 +210,43 @@
int add_app_instance(const hub_app_info *appInfo, uint32_t hubHandle, JNIEnv *env) {
// Not checking if the apps are indeed distinct
-
- app_instance_info_s *entry;
- void *appName;
- hub_app_name_t *name;
-
- assert(appInfo && appInfo->name && appInfo->name->app_name);
-
- entry = (app_instance_info_s *) malloc(sizeof(app_instance_info_s));
- appName = malloc(appInfo->name->app_name_len);
- name = (hub_app_name_t *) malloc(sizeof(hub_app_name_t));
-
+ app_instance_info_s entry;
int appInstanceHandle = generate_id();
- if (appInstanceHandle < 0 || !appName || !entry || !name) {
- ALOGE("Cannot find resources to add app instance %d, %p, %p",
- appInstanceHandle, appName, entry);
+ assert(appInfo);
- free(appName);
- free(entry);
- free(name);
-
- if (appInstanceHandle >= 0) {
- return_id(appInstanceHandle);
- }
-
+ if (appInstanceHandle < 0) {
+ ALOGE("Cannot find resources to add app instance %d",
+ appInstanceHandle);
return -1;
}
- memcpy(&(entry->appInfo), appInfo, sizeof(entry->appInfo));
- memcpy(appName, appInfo->name->app_name, appInfo->name->app_name_len);
- name->app_name = appName;
- name->app_name_len = appInfo->name->app_name_len;
- entry->appInfo.name = name;
- entry->truncName = 0;
- memcpy(&(entry->truncName), name->app_name,
- sizeof(entry->truncName) < name->app_name_len ?
- sizeof(entry->truncName) : name->app_name_len);
-
- // Not checking for sanity of hubId
- entry->hubHandle = hubHandle;
- entry->instanceId = appInstanceHandle;
+ entry.appInfo = *appInfo;
+ entry.instanceId = appInstanceHandle;
+ entry.truncName = appInfo->app_name.id;
+ entry.hubHandle = hubHandle;
db.appInstances[appInstanceHandle] = entry;
// Finally - let the service know of this app instance
env->CallIntMethod(db.jniInfo.jContextHubService,
db.jniInfo.contextHubServiceAddAppInstance,
- hubHandle, entry->instanceId, entry->truncName,
- entry->appInfo.version);
+ hubHandle, entry.instanceId, entry.truncName,
+ entry.appInfo.version);
ALOGW("Added App 0x%" PRIx64 " on hub Handle %" PRId32
- " as appInstance %d, original name_length %" PRId32, entry->truncName,
- entry->hubHandle, appInstanceHandle, name->app_name_len);
+ " as appInstance %d", entry.truncName,
+ entry.hubHandle, appInstanceHandle);
return appInstanceHandle;
}
int delete_app_instance(int id) {
- if (db.appInstances.find(id) == db.appInstances.end()) {
+ if (!db.appInstances.count(id)) {
return -1;
}
return_id(id);
-
- if (db.appInstances[id]) {
- // Losing the const cast below. This is intentional.
- free((void *)db.appInstances[id]->appInfo.name->app_name);
- free((void *)db.appInstances[id]->appInfo.name);
- free(db.appInstances[id]);
- db.appInstances.erase(id);
- }
+ db.appInstances.erase(id);
return 0;
}
@@ -353,27 +320,20 @@
}
int handle_query_apps_response(char *msg, int msgLen, uint32_t hubHandle) {
- int i;
JNIEnv *env;
if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
return -1;
}
int numApps = msgLen/sizeof(hub_app_info);
- hub_app_info *info = (hub_app_info *)malloc(msgLen); // handle possible alignment
+ hub_app_info info;
+ hub_app_info *unalignedInfoAddr = (hub_app_info*)msg;
- if (!info) {
- return -1;
+ for (int i = 0; i < numApps; i++, unalignedInfoAddr++) {
+ memcpy(&info, unalignedInfoAddr, sizeof(info));
+ add_app_instance(&info, hubHandle, env);
}
- memcpy(info, msg, msgLen);
- for (i = 0; i < numApps; i++) {
- add_app_instance(info, hubHandle, env);
- info++;
- }
-
- free(info);
-
return 0;
}
@@ -410,10 +370,6 @@
retVal = 0;
break;
- case CONTEXT_HUB_LOAD_OS:
- retVal = 0;
- break;
-
default:
retVal = -1;
break;
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index ef45c87..faa4192 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -479,12 +479,6 @@
return proxy->pauseSurface(surface);
}
-static void android_view_ThreadedRenderer_setStopped(JNIEnv* env, jobject clazz,
- jlong proxyPtr, jboolean stopped) {
- RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
- proxy->setStopped(stopped);
-}
-
static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, jlong proxyPtr,
jint width, jint height, jfloat lightRadius, jint ambientShadowAlpha, jint spotShadowAlpha) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -669,6 +663,14 @@
proxy->setContentDrawBounds(left, top, right, bottom);
}
+static jboolean android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env,
+ jobject clazz, jobject jsurface, jobject jbitmap) {
+ SkBitmap bitmap;
+ GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
+ sp<Surface> surface = android_view_Surface_getSurface(env, jsurface);
+ return RenderProxy::copySurfaceInto(surface, &bitmap);
+}
+
// ----------------------------------------------------------------------------
// FrameMetricsObserver
// ----------------------------------------------------------------------------
@@ -738,7 +740,6 @@
{ "nInitialize", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_initialize },
{ "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
{ "nPauseSurface", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_pauseSurface },
- { "nSetStopped", "(JZ)V", (void*) android_view_ThreadedRenderer_setStopped },
{ "nSetup", "(JIIFII)V", (void*) android_view_ThreadedRenderer_setup },
{ "nSetLightCenter", "(JFFF)V", (void*) android_view_ThreadedRenderer_setLightCenter },
{ "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
@@ -775,6 +776,8 @@
{ "nRemoveFrameMetricsObserver",
"(JJ)V",
(void*)android_view_ThreadedRenderer_removeFrameMetricsObserver },
+ { "nCopySurfaceInto", "(Landroid/view/Surface;Landroid/graphics/Bitmap;)Z",
+ (void*)android_view_ThreadedRenderer_copySurfaceInto },
};
int register_android_view_ThreadedRenderer(JNIEnv* env) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9a2e39c7..778f797 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -420,6 +420,7 @@
<protected-broadcast android:name="android.os.storage.action.VOLUME_STATE_CHANGED" />
<protected-broadcast android:name="android.os.storage.action.DISK_SCANNED" />
<protected-broadcast android:name="com.android.server.action.UPDATE_TWILIGHT_STATE" />
+ <protected-broadcast android:name="com.android.server.action.RESET_TWILIGHT_AUTO" />
<protected-broadcast android:name="com.android.server.device_idle.STEP_IDLE_STATE" />
<protected-broadcast android:name="com.android.server.device_idle.STEP_LIGHT_IDLE_STATE" />
<protected-broadcast android:name="com.android.server.Wifi.action.TOGGLE_PNO" />
diff --git a/core/res/res/drawable-hdpi/ic_launcher_android.png b/core/res/res/drawable-hdpi/ic_launcher_android.png
index cce5187..2e9b196 100644
--- a/core/res/res/drawable-hdpi/ic_launcher_android.png
+++ b/core/res/res/drawable-hdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_launcher_android.png b/core/res/res/drawable-ldpi/ic_launcher_android.png
index 628a8de..245e4b7 100644
--- a/core/res/res/drawable-ldpi/ic_launcher_android.png
+++ b/core/res/res/drawable-ldpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_launcher_android.png b/core/res/res/drawable-mdpi/ic_launcher_android.png
index 6a97d5b..baacd4f 100644
--- a/core/res/res/drawable-mdpi/ic_launcher_android.png
+++ b/core/res/res/drawable-mdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_launcher_android.png b/core/res/res/drawable-xhdpi/ic_launcher_android.png
index b1097d6..00b69a5 100644
--- a/core/res/res/drawable-xhdpi/ic_launcher_android.png
+++ b/core/res/res/drawable-xhdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_launcher_android.png b/core/res/res/drawable-xxhdpi/ic_launcher_android.png
new file mode 100644
index 0000000..ad05cd5
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable/ic_input_extract_action_done.xml b/core/res/res/drawable/ic_input_extract_action_done.xml
new file mode 100644
index 0000000..f6e872e
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_done.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2016 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 android:height="24dp" android:viewportHeight="48.0"
+ android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FFFFFF" android:pathData="M18,32.34L9.66,24l-2.83,2.83L18,38l24,-24 -2.83,-2.83z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_go.xml b/core/res/res/drawable/ic_input_extract_action_go.xml
new file mode 100644
index 0000000..edbc826
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_go.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2016 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 android:height="24dp" android:viewportHeight="48.0"
+ android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FFFFFF" android:pathData="M6,22h28.34l-7.17,-7.17L30,12l12,12 -12,12 -2.83,-2.83L34.34,26H6z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_next.xml b/core/res/res/drawable/ic_input_extract_action_next.xml
new file mode 100644
index 0000000..ffef346
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_next.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2016 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 android:height="24dp" android:viewportHeight="48.0"
+ android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FFFFFF" android:pathData="M23.17,14.83L30.34,22H2v4h28.34l-7.17,7.17L26,36l12,-12 -12,-12 -2.83,2.83zM40,12v24h4V12h-4z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_previous.xml b/core/res/res/drawable/ic_input_extract_action_previous.xml
new file mode 100644
index 0000000..89777b0
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_previous.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2016 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 android:height="24dp" android:viewportHeight="48.0"
+ android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FFFFFF" android:pathData="M22.83,14.83L15.66,22H44v4H15.66l7.17,7.17L20,36 8,24l12,-12 2.83,2.83zM6,12v24H2V12h4z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_return.xml b/core/res/res/drawable/ic_input_extract_action_return.xml
new file mode 100644
index 0000000..cb2de5a
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_return.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2016 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 android:height="24dp" android:viewportHeight="48.0"
+ android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FFFFFF" android:pathData="M38,14v8H11.66l7.17,-7.17L16,12 4,24l12,12 2.83,-2.83L11.66,26H42V14z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_search.xml b/core/res/res/drawable/ic_input_extract_action_search.xml
new file mode 100644
index 0000000..dcbcdbf
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_search.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2016 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 android:height="24dp" android:viewportHeight="48.0"
+ android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FFFFFF" android:pathData="M31,28h-1.59l-0.55,-0.55C30.82,25.18 32,22.23 32,19c0,-7.18 -5.82,-13 -13,-13S6,11.82 6,19s5.82,13 13,13c3.23,0 6.18,-1.18 8.45,-3.13l0.55,0.55L28,31l10,9.98L40.98,38 31,28zM19,28c-4.97,0 -9,-4.03 -9,-9s4.03,-9 9,-9 9,4.03 9,9 -4.03,9 -9,9z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_send.xml b/core/res/res/drawable/ic_input_extract_action_send.xml
new file mode 100644
index 0000000..6494bee5
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_send.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright (C) 2016 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="36dp"
+ android:height="36dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:pathData="M4.02,42L46,24 4.02,6 4,20l30,4 -30,4z"
+ android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/core/res/res/drawable/input_extract_action_bg_material_dark.xml b/core/res/res/drawable/input_extract_action_bg_material_dark.xml
new file mode 100644
index 0000000..9c6a6c3
--- /dev/null
+++ b/core/res/res/drawable/input_extract_action_bg_material_dark.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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:drawable="@drawable/input_extract_action_bg_pressed_material_dark"
+ android:state_pressed="true"/>
+ <item android:drawable="@drawable/input_extract_action_bg_normal_material_dark"/>
+</selector>
diff --git a/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml b/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml
new file mode 100644
index 0000000..8449978
--- /dev/null
+++ b/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+ <solid android:color="@color/material_deep_teal_200"/>
+</shape>
diff --git a/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml b/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml
new file mode 100644
index 0000000..adade104
--- /dev/null
+++ b/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+ <solid android:color="@color/material_deep_teal_100"/>
+</shape>
diff --git a/core/res/res/layout-watch/input_method_extract_view.xml b/core/res/res/layout-watch/input_method_extract_view.xml
new file mode 100644
index 0000000..e3cd2ce
--- /dev/null
+++ b/core/res/res/layout-watch/input_method_extract_view.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+<android.inputmethodservice.CompactExtractEditLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:baselineAligned="false">
+
+ <android.inputmethodservice.ExtractEditText
+ android:id="@id/inputExtractEditText"
+ android:layout_width="0dp"
+ android:layout_height="24dp"
+ android:background="@null"
+ android:singleLine="true"
+ android:inputType="text"
+ android:layout_weight="1"
+ android:fontFamily="sans-serif-condensed-light"
+ android:textColor="@color/primary_text_default_material_dark"
+ android:textColorHighlight="@color/accent_material_dark"
+ android:textSize="18dp"
+ android:cursorVisible="false"
+ android:gravity="bottom|right"
+ />
+
+ <FrameLayout
+ android:id="@id/inputExtractAccessories"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:visibility="visible">
+ <ImageButton
+ android:id="@id/inputExtractAction"
+ android:layout_width="@dimen/input_extract_action_button_width"
+ android:layout_height="@dimen/input_extract_action_button_width"
+ android:background="@drawable/input_extract_action_bg_material_dark"
+ android:padding="4dp"
+ android:scaleType="centerInside" />
+ </FrameLayout>
+</android.inputmethodservice.CompactExtractEditLayout>
diff --git a/core/res/res/layout/notification_template_material_messaging.xml b/core/res/res/layout/notification_template_material_messaging.xml
new file mode 100644
index 0000000..7d718e0
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_messaging.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 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
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:tag="messaging"
+ >
+ <include layout="@layout/notification_template_header" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="top"
+ android:layout_marginTop="@dimen/notification_content_margin_top"
+ android:clipToPadding="false"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:paddingStart="@dimen/notification_content_margin_start"
+ android:paddingEnd="@dimen/notification_content_margin_end"
+ android:minHeight="@dimen/notification_min_content_height"
+ android:clipToPadding="false"
+ android:orientation="vertical"
+ >
+ <include layout="@layout/notification_template_part_line1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/notification_messaging"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="@dimen/notification_content_margin_bottom"
+ android:spacing="@dimen/notification_messaging_spacing"
+ android:maxHeight="212dp">
+ <com.android.internal.widget.ImageFloatingTextView android:id="@+id/inbox_text0"
+ style="@style/Widget.Material.Notification.MessagingText"
+ />
+ <com.android.internal.widget.ImageFloatingTextView android:id="@+id/inbox_text1"
+ style="@style/Widget.Material.Notification.MessagingText"
+ />
+ <com.android.internal.widget.ImageFloatingTextView android:id="@+id/inbox_text2"
+ style="@style/Widget.Material.Notification.MessagingText"
+ />
+ <com.android.internal.widget.ImageFloatingTextView android:id="@+id/inbox_text3"
+ style="@style/Widget.Material.Notification.MessagingText"
+ />
+ <com.android.internal.widget.ImageFloatingTextView android:id="@+id/inbox_text4"
+ style="@style/Widget.Material.Notification.MessagingText"
+ />
+ <com.android.internal.widget.ImageFloatingTextView android:id="@+id/inbox_text5"
+ style="@style/Widget.Material.Notification.MessagingText"
+ />
+ <com.android.internal.widget.ImageFloatingTextView android:id="@+id/inbox_text6"
+ style="@style/Widget.Material.Notification.MessagingText"
+ />
+ </com.android.internal.widget.MessagingLinearLayout>
+ </LinearLayout>
+ <include layout="@layout/notification_material_action_list" />
+ </LinearLayout>
+ <include layout="@layout/notification_template_right_icon" />
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_right_icon.xml b/core/res/res/layout/notification_template_right_icon.xml
index 15ccc67..b652127 100644
--- a/core/res/res/layout/notification_template_right_icon.xml
+++ b/core/res/res/layout/notification_template_right_icon.xml
@@ -16,9 +16,9 @@
-->
<ImageView android:id="@+id/right_icon" xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="40dp"
- android:layout_height="40dp"
- android:layout_marginEnd="16dp"
+ android:layout_width="@dimen/notification_large_icon_width"
+ android:layout_height="@dimen/notification_large_icon_width"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
android:layout_marginTop="36dp"
android:layout_gravity="top|end"
android:scaleType="centerCrop"
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index fe43e1c..5850e50 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -30,37 +30,33 @@
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
android:elevation="8dp"
- android:background="@color/white">
-
- <TextView
- android:id="@+id/profile_button"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:layout_marginEnd="8dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:visibility="gone"
- style="?attr/borderlessButtonStyle"
- android:textAppearance="?attr/textAppearanceButton"
- android:textColor="@color/material_deep_teal_500"
- android:gravity="center_vertical"
- android:layout_alignParentTop="true"
- android:layout_alignParentRight="true"
- android:singleLine="true" />
-
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:minHeight="56dp"
- android:textAppearance="?attr/textAppearanceMedium"
- android:gravity="start|center_vertical"
- android:paddingStart="?attr/dialogPreferredPadding"
- android:paddingEnd="?attr/dialogPreferredPadding"
- android:paddingTop="8dp"
- android:layout_below="@id/profile_button"
- android:layout_alignParentLeft="true"
- android:paddingBottom="8dp" />
+ android:background="@color/white" >
+ <TextView android:id="@+id/profile_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginEnd="8dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:visibility="gone"
+ style="?attr/borderlessButtonStyle"
+ android:textAppearance="?attr/textAppearanceButton"
+ android:textColor="@color/material_deep_teal_500"
+ android:gravity="center_vertical"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentRight="true"
+ android:singleLine="true"/>
+ <TextView android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="56dp"
+ android:textAppearance="?attr/textAppearanceMedium"
+ android:gravity="start|center_vertical"
+ android:paddingStart="?attr/dialogPreferredPadding"
+ android:paddingEnd="?attr/dialogPreferredPadding"
+ android:paddingTop="8dp"
+ android:layout_below="@id/profile_button"
+ android:layout_alignParentLeft="true"
+ android:paddingBottom="8dp" />
</RelativeLayout>
<ListView
@@ -72,23 +68,23 @@
android:background="@color/white"
android:elevation="8dp"
android:nestedScrollingEnabled="true"
- android:scrollIndicators="top|bottom"
android:divider="@null" />
- <TextView
- android:id="@+id/empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alwaysShow="true"
- android:text="@string/noApplications"
- android:padding="32dp"
- android:gravity="center"
- android:visibility="gone" />
+ <TextView android:id="@+id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/white"
+ android:elevation="8dp"
+ android:layout_alwaysShow="true"
+ android:text="@string/noApplications"
+ android:padding="32dp"
+ android:gravity="center"
+ android:visibility="gone" />
<LinearLayout
android:id="@+id/button_bar"
android:visibility="gone"
- style="?attr/buttonBarStyle"
+ style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_ignoreOffset="true"
@@ -103,30 +99,26 @@
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:elevation="8dp">
-
- <Button
- android:id="@+id/button_once"
- android:layout_width="wrap_content"
- android:layout_gravity="start"
- android:maxLines="2"
- style="?attr/buttonBarNegativeButtonStyle"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- android:layout_height="wrap_content"
- android:enabled="false"
- android:text="@string/activity_resolver_use_once"
- android:onClick="onButtonClick" />
-
- <Button
- android:id="@+id/button_always"
- android:layout_width="wrap_content"
- android:layout_gravity="end"
- android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?attr/buttonBarPositiveButtonStyle"
- android:layout_height="wrap_content"
- android:enabled="false"
- android:text="@string/activity_resolver_use_always"
- android:onClick="onButtonClick" />
+ <Button android:id="@+id/button_once"
+ android:layout_width="wrap_content"
+ android:layout_gravity="start"
+ android:maxLines="2"
+ style="?android:attr/buttonBarNegativeButtonStyle"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ android:layout_height="wrap_content"
+ android:enabled="false"
+ android:text="@string/activity_resolver_use_once"
+ android:onClick="onButtonClick" />
+ <Button android:id="@+id/button_always"
+ android:layout_width="wrap_content"
+ android:layout_gravity="end"
+ android:maxLines="2"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ style="?android:attr/buttonBarPositiveButtonStyle"
+ android:layout_height="wrap_content"
+ android:enabled="false"
+ android:text="@string/activity_resolver_use_always"
+ android:onClick="onButtonClick" />
</LinearLayout>
</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index ed7ef5e..31361e5 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -22,7 +22,8 @@
android:layout_height="match_parent"
android:maxWidth="@dimen/resolver_max_width"
android:maxCollapsedHeight="144dp"
- android:id="@id/contentPanel">
+ android:id="@id/contentPanel"
+ >
<LinearLayout
android:layout_width="match_parent"
@@ -30,75 +31,66 @@
android:layout_alwaysShow="true"
android:orientation="vertical"
android:background="@color/white"
- android:elevation="8dp">
+ android:elevation="8dp" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="64dp"
- android:orientation="horizontal">
+ android:orientation="horizontal" >
- <ImageView
- android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="start|top"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginTop="20dp"
- android:scaleType="fitCenter" />
-
- <TextView
- android:id="@+id/title"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="?attr/listPreferredItemHeight"
- android:layout_marginStart="16dp"
- android:textAppearance="?attr/textAppearanceMedium"
- android:gravity="start|center_vertical"
- android:paddingEnd="16dp" />
-
- <LinearLayout
- android:id="@+id/profile_button"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:layout_marginTop="4dp"
- android:layout_marginEnd="4dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
- android:focusable="true"
- android:visibility="gone"
- style="?attr/borderlessButtonStyle">
-
- <ImageView
- android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="start|center_vertical"
- android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
- android:layout_marginTop="12dp"
- android:layout_marginBottom="12dp"
- android:scaleType="fitCenter" />
-
- <TextView
- android:id="@id/text1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="start|center_vertical"
- android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
- android:textAppearance="?attr/textAppearanceButton"
- android:textColor="?attr/textColorPrimary"
- android:minLines="1"
- android:maxLines="1"
- android:ellipsize="marquee" />
+ <ImageView android:id="@+id/icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="start|top"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"
+ android:layout_marginTop="20dp"
+ android:scaleType="fitCenter" />
+ <TextView android:id="@+id/title"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:layout_marginStart="16dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="start|center_vertical"
+ android:paddingEnd="16dp" />
+ <LinearLayout android:id="@+id/profile_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginEnd="4dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:focusable="true"
+ android:visibility="gone"
+ style="?attr/borderlessButtonStyle">
+ <ImageView android:id="@+id/icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="start|center_vertical"
+ android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp"
+ android:scaleType="fitCenter" />
+ <TextView android:id="@id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|center_vertical"
+ android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
+ android:textAppearance="?attr/textAppearanceButton"
+ android:textColor="?attr/textColorPrimary"
+ android:minLines="1"
+ android:maxLines="1"
+ android:ellipsize="marquee" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/button_bar"
android:visibility="gone"
- style="?attr/buttonBarStyle"
+ style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
@@ -112,36 +104,30 @@
android:paddingEnd="12dp"
android:background="@color/white"
android:elevation="8dp">
-
- <Button
- android:id="@+id/button_once"
- android:layout_width="wrap_content"
- android:layout_gravity="start"
- android:maxLines="2"
- style="?attr/buttonBarNegativeButtonStyle"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- android:layout_height="wrap_content"
- android:enabled="false"
- android:text="@string/activity_resolver_use_once"
- android:onClick="onButtonClick" />
-
- <Button
- android:id="@+id/button_always"
- android:layout_width="wrap_content"
- android:layout_gravity="end"
- android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?attr/buttonBarPositiveButtonStyle"
- android:layout_height="wrap_content"
- android:enabled="false"
- android:text="@string/activity_resolver_use_always"
- android:onClick="onButtonClick" />
+ <Button android:id="@+id/button_once"
+ android:layout_width="wrap_content"
+ android:layout_gravity="start"
+ android:maxLines="2"
+ style="?android:attr/buttonBarNegativeButtonStyle"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ android:layout_height="wrap_content"
+ android:enabled="false"
+ android:text="@string/activity_resolver_use_once"
+ android:onClick="onButtonClick" />
+ <Button android:id="@+id/button_always"
+ android:layout_width="wrap_content"
+ android:layout_gravity="end"
+ android:maxLines="2"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ style="?android:attr/buttonBarPositiveButtonStyle"
+ android:layout_height="wrap_content"
+ android:enabled="false"
+ android:text="@string/activity_resolver_use_always"
+ android:onClick="onButtonClick" />
</LinearLayout>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="?attr/dividerVertical" />
+ <View android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:attr/dividerVertical" />
</LinearLayout>
<ListView
@@ -154,6 +140,6 @@
android:elevation="8dp"
android:nestedScrollingEnabled="true"
android:divider="@null"
- android:scrollIndicators="top|bottom" />
+ />
</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/values-round-watch/dimens.xml b/core/res/res/values-round-watch/dimens.xml
new file mode 100644
index 0000000..a12f499
--- /dev/null
+++ b/core/res/res/values-round-watch/dimens.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- each of these are relative to the display size -->
+ <item name="input_extract_layout_height" type="fraction">25.2%</item>
+ <item name="input_extract_layout_padding_left" type="fraction">7.5%</item>
+ <item name="input_extract_layout_padding_left_no_action" type="fraction">@fraction/input_extract_layout_padding_right</item>
+ <item name="input_extract_layout_padding_right" type="fraction">21.4%</item>
+ <item name="input_extract_text_margin_bottom" type="fraction">5.5%</item>
+ <item name="input_extract_action_margin_bottom" type="fraction">2.1%</item>
+ <item name="input_extract_action_button_width" type="dimen">32dp</item>
+ <item name="input_extract_action_button_height" type="dimen">32dp</item>
+</resources>
diff --git a/core/res/res/values-w170dp-notround-watch/dimens.xml b/core/res/res/values-w170dp-notround-watch/dimens.xml
new file mode 100644
index 0000000..c91cbc1
--- /dev/null
+++ b/core/res/res/values-w170dp-notround-watch/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- each of these are relative to the display size -->
+ <item name="input_extract_layout_padding_right" type="fraction">7.5%</item>
+</resources>
diff --git a/core/res/res/values-h426dp-port/integers.xml b/core/res/res/values-w320dp-h426dp/integers.xml
similarity index 100%
rename from core/res/res/values-h426dp-port/integers.xml
rename to core/res/res/values-w320dp-h426dp/integers.xml
diff --git a/core/res/res/values-w426dp-land/integers.xml b/core/res/res/values-w426dp-h320dp/integers.xml
similarity index 100%
rename from core/res/res/values-w426dp-land/integers.xml
rename to core/res/res/values-w426dp-h320dp/integers.xml
diff --git a/core/res/res/values-watch/dimens.xml b/core/res/res/values-watch/dimens.xml
new file mode 100644
index 0000000..f103aa9
--- /dev/null
+++ b/core/res/res/values-watch/dimens.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- each of these are relative to the display size -->
+ <item name="input_extract_layout_height" type="fraction">17.5%</item>
+ <item name="input_extract_layout_padding_left" type="fraction">3.6%</item>
+ <item name="input_extract_layout_padding_left_no_action" type="fraction">@fraction/input_extract_layout_padding_right</item>
+ <item name="input_extract_layout_padding_right" type="fraction">2.5%</item>
+ <item name="input_extract_text_margin_bottom" type="fraction">0%</item>
+ <item name="input_extract_action_margin_bottom" type="fraction">0%</item>
+ <item name="input_extract_action_button_width" type="dimen">24dp</item>
+ <item name="input_extract_action_button_height" type="dimen">24dp</item>
+</resources>
diff --git a/core/res/res/values-watch/themes.xml b/core/res/res/values-watch/themes.xml
index 756a94b..6d6065f 100644
--- a/core/res/res/values-watch/themes.xml
+++ b/core/res/res/values-watch/themes.xml
@@ -18,6 +18,7 @@
<style name="Theme.Dialog.AppError" parent="Theme.Micro.Dialog.AppError" />
<style name="Theme.Holo.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
<style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+ <style name="Theme.InputMethod" parent="Theme.Micro.InputMethod" />
<style name="Theme.Material.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
<style name="Theme.Material.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
</resources>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 61753b1..66509fb 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -19,14 +19,16 @@
<style name="Theme.DeviceDefault.Dialog" parent="Theme.Micro.Dialog" />
<style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Micro.Dialog" />
<style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+ <style name="Theme.DeviceDefault.InputMethod" parent="Theme.Micro.InputMethod" />
+ <style name="Theme.DeviceDefault.Panel" parent="Theme.Micro.Panel" />
<style name="Theme.DeviceDefault.Light" parent="Theme.Micro.Light" />
<style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Micro.Light" />
<style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Micro.Light" />
<style name="Theme.DeviceDefault.Light.Dialog" parent="Theme.Micro.Dialog" />
<style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Micro.Dialog" />
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+ <style name="Theme.DeviceDefault.Light.Panel" parent="Theme.Micro.Light.Panel" />
<style name="Theme.DeviceDefault.Settings" parent="Theme.Micro" />
<style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Micro" />
-
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 0ed1f13..a320ef6 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8129,6 +8129,11 @@
<attr name="maxCollapsedHeightSmall" format="dimension" />
</declare-styleable>
+ <declare-styleable name="MessagingLinearLayout">
+ <attr name="maxHeight" />
+ <attr name="spacing" />
+ </declare-styleable>
+
<declare-styleable name="ResolverDrawerLayout_LayoutParams">
<attr name="layout_alwaysShow" format="boolean" />
<attr name="layout_ignoreOffset" format="boolean" />
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 7399fa9..c8ca116 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -75,7 +75,9 @@
<color name="material_grey_100">#fff5f5f5</color>
<color name="material_grey_50">#fffafafa</color>
+ <color name="material_deep_teal_100">#ffb2dfdb</color>
<color name="material_deep_teal_200">#ff80cbc4</color>
+ <color name="material_deep_teal_300">#ff4db6ac</color>
<color name="material_deep_teal_500">#ff009688</color>
<color name="material_blue_grey_800">#ff37474f</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index dfa5143b..aada05d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -420,9 +420,6 @@
<!-- Boolean indicating we re-try re-associating once upon disconnection and RSSI is high failure -->
<bool translatable="true" name="config_wifi_enable_disconnection_debounce">true</bool>
- <!-- Boolean indicating autojoin will prefer 5GHz and choose 5GHz BSSIDs -->
- <bool translatable="true" name="config_wifi_enable_5GHz_preference">true</bool>
-
<!-- Boolean indicating whether or not to revert to default country code when cellular
radio is unable to find any MCC information to infer wifi country code from -->
<bool translatable="false" name="config_wifi_revert_country_code_on_cellular_loss">false</bool>
@@ -439,8 +436,6 @@
<!-- Integer specifying the basic autojoin parameters -->
<integer translatable="false" name="config_wifi_framework_5GHz_preference_boost_threshold">-65</integer>
<integer translatable="false" name="config_wifi_framework_5GHz_preference_boost_factor">40</integer>
- <integer translatable="false" name="config_wifi_framework_current_association_hysteresis_high">16</integer>
- <integer translatable="false" name="config_wifi_framework_current_association_hysteresis_low">10</integer>
<integer translatable="false" name="config_wifi_framework_5GHz_preference_penalty_threshold">-75</integer>
<integer translatable="false" name="config_wifi_framework_RSSI_SCORE_OFFSET">85</integer>
<integer translatable="false" name="config_wifi_framework_RSSI_SCORE_SLOPE">4</integer>
@@ -495,15 +490,9 @@
<!-- Integer indicating disconnect mode short scan interval in milliseconds -->
<integer translatable="false" name="config_wifi_disconnected_short_scan_interval">15000</integer>
- <!-- Integer indicating disconnect mode long scan interval in milliseconds -->
- <integer translatable="false" name="config_wifi_disconnected_long_scan_interval">120000</integer>
-
<!-- Integer indicating associated partial scan short interval in milliseconds -->
<integer translatable="false" name="config_wifi_associated_short_scan_interval">20000</integer>
- <!-- Integer indicating associated partial scan long interval in milliseconds -->
- <integer translatable="false" name="config_wifi_associated_long_scan_interval">180000</integer>
-
<!-- Integer indicating associated full scan backoff, representing a fraction: xx/8 -->
<integer translatable="false" name="config_wifi_framework_associated_full_scan_backoff">12</integer>
@@ -516,18 +505,6 @@
<!-- Integer indicating associated full scan max num active channels -->
<integer translatable="false" name="config_wifi_framework_associated_partial_scan_max_num_active_channels">6</integer>
- <!-- Integer indicating associated full scan max num passive channels -->
- <integer translatable="false" name="config_wifi_framework_associated_partial_scan_max_num_passive_channels">3</integer>
-
- <!-- Integer indicating number of association errors leading to blacklisting of the network -->
- <integer translatable="false" name="config_wifi_framework_max_connection_errors_to_blacklist">4</integer>
-
- <!-- Integer indicating number of authentication errors leading to blacklisting of the network -->
- <integer translatable="false" name="config_wifi_framework_max_auth_errors_to_blacklist">4</integer>
-
- <!-- Integer indicating minimum blacklisting delay of a wofo configuration due to connectin or auth errors -->
- <integer translatable="false" name="config_wifi_framework_network_black_list_min_time_milli">120000</integer>
-
<!-- Integer indicating RSSI boost given to current network -->
<integer translatable="false" name="config_wifi_framework_current_network_boost">16</integer>
@@ -543,12 +520,6 @@
<!-- Wifi driver supports batched scan -->
<bool translatable="false" name="config_wifi_batched_scan_supported">false</bool>
- <!-- Wifi HAL supported PNO -->
- <bool translatable="false" name="config_wifi_hal_pno_enable">false</bool>
-
- <!-- Wifi SSID white list (can't be enabled if config_wifi_hal_pno_enable is not) -->
- <bool translatable="false" name="config_wifi_ssid_white_list_enable">true</bool>
-
<!-- Idle Receive current for wifi radio. 0 by default-->
<integer translatable="false" name="config_wifi_idle_receive_cur_ma">0</integer>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index dd54d57..9178305 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -162,9 +162,9 @@
<dimen name="notification_min_height">92dp</dimen>
<!-- The width of the big icons in notifications. -->
- <dimen name="notification_large_icon_width">64dp</dimen>
+ <dimen name="notification_large_icon_width">40dp</dimen>
<!-- The width of the big icons in notifications. -->
- <dimen name="notification_large_icon_height">64dp</dimen>
+ <dimen name="notification_large_icon_height">40dp</dimen>
<!-- The minimum width of the app name in the header if it shrinks -->
<dimen name="notification_header_shrink_min_width">72dp</dimen>
@@ -181,6 +181,9 @@
<!-- The margin of the content to an image-->
<dimen name="notification_content_image_margin_end">8dp</dimen>
+ <!-- The spacing between messages in Notification.MessagingStyle -->
+ <dimen name="notification_messaging_spacing">6dp</dimen>
+
<!-- Preferred width of the search view. -->
<dimen name="search_view_preferred_width">320dip</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 96731cf..dd60778 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2552,21 +2552,29 @@
<!-- Title of intent resolver dialog when selecting an application to run
and a previously used application is known. -->
<string name="whichApplicationNamed">Complete action using %1$s</string>
+ <!-- Generic label for a link to a intent resolver. -->
+ <string name="whichApplicationLabel">Complete action</string>
<!-- Title of intent resolver dialog when selecting a viewer application to run. -->
<string name="whichViewApplication">Open with</string>
<!-- Title of intent resolver dialog when selecting a viewer application to run
and a previously used application is known. -->
<string name="whichViewApplicationNamed">Open with %1$s</string>
+ <!-- Label for a link to a intent resolver dialog to view something -->
+ <string name="whichViewApplicationLabel">Open</string>
<!-- Title of intent resolver dialog when selecting an editor application to run. -->
<string name="whichEditApplication">Edit with</string>
<!-- Title of intent resolver dialog when selecting an editor application to run
and a previously used application is known. -->
<string name="whichEditApplicationNamed">Edit with %1$s</string>
+ <!-- Label for a link to a intent resolver dialog when selecting an editor application -->
+ <string name="whichEditApplicationLabel">Edit</string>
<!-- Title of intent resolver dialog when selecting a sharing application to run. -->
<string name="whichSendApplication">Share with</string>
<!-- Title of intent resolver dialog when selecting a sharing application to run
and a previously used application is known. -->
<string name="whichSendApplicationNamed">Share with %1$s</string>
+ <!-- Label for a link to a intent resolver dialog to sharing something -->
+ <string name="whichSendApplicationLabel">Share</string>
<!-- Title of intent resolver dialog when selecting an application to run to
send content to a specific recipient. Often used for email. -->
<string name="whichSendToApplication">Send using</string>
@@ -2574,11 +2582,23 @@
send content to a specific recipient and a previously used application is known.
Often used for email. -->
<string name="whichSendToApplicationNamed">Send using %1$s</string>
+ <!-- Label for a link to a intent resolver dialog to send content to a specific recipient. -->
+ <string name="whichSendToApplicationLabel">Send</string>
<!-- Title of intent resolver dialog when selecting a HOME application to run. -->
<string name="whichHomeApplication">Select a Home app</string>
<!-- Title of intent resolver dialog when selecting a HOME application to run
and a previously used application is known. -->
<string name="whichHomeApplicationNamed">Use %1$s as Home</string>
+ <!-- Label for a link to a intent resolver dialog when selecting a HOME -->
+ <string name="whichHomeApplicationLabel">Capture image</string>
+ <!-- Option to always use the selected application resolution in the future. See the "Complete action using" dialog title-->
+ <!-- Title of intent resolver dialog when capturing an image. -->
+ <string name="whichImageCaptureApplication">Capture image with</string>
+ <!-- Title of intent resolver dialog when capturing an image
+ and a previously used application is known. -->
+ <string name="whichImageCaptureApplicationNamed">Capture image with %1$s</string>
+ <!-- Label for a link to a intent resolver dialog when capturing an image -->
+ <string name="whichImageCaptureApplicationLabel">Capture image</string>
<!-- Option to always use the selected application resolution in the future. See the "Complete action using" dialog title-->
<string name="alwaysUse">Use by default for this action.</string>
<!-- Title of the list of alternate options to complete an action shown when the
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 2420c1a..8a33406 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -456,6 +456,14 @@
<style name="Widget.Material.Notification.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" />
+ <style name="Widget.Material.Notification.MessagingText" parent="Widget.Material.Light.TextView">
+ <item name="layout_width">match_parent</item>
+ <item name="layout_height">wrap_content</item>
+ <item name="ellipsize">end</item>
+ <item name="visibility">gone</item>
+ <item name="textAppearance">@style/TextAppearance.Material.Notification</item>
+ </style>
+
<!-- Widget Styles -->
<style name="Material"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8d8b832..98661cf 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -295,7 +295,6 @@
<java-symbol type="bool" name="config_wifi_framework_enable_associated_network_selection" />
<java-symbol type="bool" name="config_wifi_only_link_same_credential_configurations" />
<java-symbol type="bool" name="config_wifi_enable_disconnection_debounce" />
- <java-symbol type="bool" name="config_wifi_enable_5GHz_preference" />
<java-symbol type="bool" name="config_wifi_revert_country_code_on_cellular_loss" />
<java-symbol type="bool" name="config_wifi_enable_wifi_firmware_debugging" />
<java-symbol type="integer" name="config_wifi_logger_ring_buffer_size_limit_kb" />
@@ -312,8 +311,6 @@
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_factor" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" />
- <java-symbol type="integer" name="config_wifi_framework_current_association_hysteresis_high" />
- <java-symbol type="integer" name="config_wifi_framework_current_association_hysteresis_low" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" />
<java-symbol type="integer" name="config_wifi_framework_RSSI_SCORE_OFFSET" />
<java-symbol type="integer" name="config_wifi_framework_RSSI_SCORE_SLOPE" />
@@ -322,14 +319,11 @@
<java-symbol type="integer" name="config_wifi_framework_PASSPOINT_SECURITY_AWARD" />
<java-symbol type="integer" name="config_wifi_framework_SECURITY_AWARD" />
<java-symbol type="integer" name="config_wifi_disconnected_short_scan_interval" />
- <java-symbol type="integer" name="config_wifi_disconnected_long_scan_interval" />
<java-symbol type="integer" name="config_wifi_associated_short_scan_interval" />
- <java-symbol type="integer" name="config_wifi_associated_long_scan_interval" />
<java-symbol type="integer" name="config_wifi_framework_associated_full_scan_backoff" />
<java-symbol type="integer" name="config_wifi_framework_associated_full_scan_max_interval" />
<java-symbol type="integer" name="config_wifi_framework_associated_full_scan_max_total_dwell_time" />
<java-symbol type="integer" name="config_wifi_framework_associated_partial_scan_max_num_active_channels" />
- <java-symbol type="integer" name="config_wifi_framework_associated_partial_scan_max_num_passive_channels" />
<java-symbol type="integer" name="config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz" />
<java-symbol type="integer" name="config_wifi_framework_wifi_score_low_rssi_threshold_24GHz" />
<java-symbol type="integer" name="config_wifi_framework_wifi_score_good_rssi_threshold_24GHz" />
@@ -359,9 +353,6 @@
<java-symbol type="integer" name="config_wifi_framework_associated_partial_scan_rx_packet_threshold" />
<java-symbol type="integer" name="config_wifi_framework_network_switch_tx_packet_threshold" />
<java-symbol type="integer" name="config_wifi_framework_network_switch_rx_packet_threshold" />
- <java-symbol type="integer" name="config_wifi_framework_max_connection_errors_to_blacklist" />
- <java-symbol type="integer" name="config_wifi_framework_max_auth_errors_to_blacklist" />
- <java-symbol type="integer" name="config_wifi_framework_network_black_list_min_time_milli" />
<java-symbol type="integer" name="config_wifi_framework_current_network_boost" />
<java-symbol type="integer" name="config_bluetooth_max_advertisers" />
<java-symbol type="integer" name="config_bluetooth_max_scan_filters" />
@@ -399,9 +390,7 @@
<java-symbol type="integer" name="config_wifi_supplicant_scan_interval" />
<java-symbol type="integer" name="config_wifi_no_network_periodic_scan_interval" />
<java-symbol type="integer" name="config_wifi_scan_interval_p2p_connected" />
- <java-symbol type="bool" name="config_wifi_hal_pno_enable" />
<java-symbol type="integer" name="config_windowOutsetBottom" />
- <java-symbol type="bool" name="config_wifi_ssid_white_list_enable" />
<java-symbol type="integer" name="db_connection_pool_size" />
<java-symbol type="integer" name="db_journal_size_limit" />
<java-symbol type="integer" name="db_wal_autocheckpoint" />
@@ -1397,7 +1386,6 @@
<java-symbol type="xml" name="password_kbd_qwerty" />
<java-symbol type="xml" name="autotext" />
- <java-symbol type="xml" name="eri" />
<java-symbol type="xml" name="password_kbd_numeric" />
<java-symbol type="xml" name="password_kbd_qwerty_shifted" />
<java-symbol type="xml" name="password_kbd_symbols" />
@@ -2210,14 +2198,22 @@
<java-symbol type="attr" name="touchscreenBlocksFocus" />
<java-symbol type="layout" name="resolver_list_with_default" />
<java-symbol type="string" name="whichApplicationNamed" />
+ <java-symbol type="string" name="whichApplicationLabel" />
<java-symbol type="string" name="whichViewApplication" />
<java-symbol type="string" name="whichViewApplicationNamed" />
+ <java-symbol type="string" name="whichViewApplicationLabel" />
<java-symbol type="string" name="whichEditApplication" />
<java-symbol type="string" name="whichEditApplicationNamed" />
+ <java-symbol type="string" name="whichEditApplicationLabel" />
<java-symbol type="string" name="whichSendApplication" />
<java-symbol type="string" name="whichSendApplicationNamed" />
+ <java-symbol type="string" name="whichSendApplicationLabel" />
<java-symbol type="string" name="whichSendToApplication" />
<java-symbol type="string" name="whichSendToApplicationNamed" />
+ <java-symbol type="string" name="whichSendToApplicationLabel" />
+ <java-symbol type="string" name="whichImageCaptureApplication" />
+ <java-symbol type="string" name="whichImageCaptureApplicationNamed" />
+ <java-symbol type="string" name="whichImageCaptureApplicationLabel" />
<java-symbol type="attr" name="lightY" />
<java-symbol type="attr" name="lightZ" />
<java-symbol type="attr" name="lightRadius" />
@@ -2247,6 +2243,7 @@
<java-symbol type="array" name="networks_not_clear_data" />
<java-symbol type="bool" name="config_switch_phone_on_voice_reg_state_change" />
<java-symbol type="string" name="whichHomeApplicationNamed" />
+ <java-symbol type="string" name="whichHomeApplicationLabel" />
<java-symbol type="bool" name="config_sms_force_7bit_encoding" />
<java-symbol type="bool" name="config_defaultWindowFeatureOptionsPanel" />
<java-symbol type="bool" name="config_defaultWindowFeatureContextMenu" />
@@ -2505,6 +2502,8 @@
<java-symbol type="bool" name="config_strongAuthRequiredOnBoot" />
<java-symbol type="layout" name="app_anr_dialog" />
+ <java-symbol type="layout" name="notification_template_material_messaging" />
+
<java-symbol type="id" name="aerr_wait" />
<java-symbol type="id" name="notification_content_container" />
@@ -2534,11 +2533,34 @@
<java-symbol type="string" name="carrier_app_notification_text" />
<java-symbol type="string" name="negative_duration" />
+ <java-symbol type="dimen" name="notification_messaging_spacing" />
+
<!-- WallpaperManager config -->
<java-symbol type="string" name="config_wallpaperCropperPackage" />
<java-symbol type="id" name="textSpacerNoTitle" />
<java-symbol type="id" name="titleDividerNoCustom" />
+ <java-symbol type="id" name="notification_messaging" />
+
<java-symbol type="bool" name="config_sustainedPerformanceModeSupported" />
+
+ <!-- Wearable input extract edit view -->
+ <java-symbol type="drawable" name="ic_input_extract_action_go" />
+ <java-symbol type="drawable" name="ic_input_extract_action_search" />
+ <java-symbol type="drawable" name="ic_input_extract_action_send" />
+ <java-symbol type="drawable" name="ic_input_extract_action_next" />
+ <java-symbol type="drawable" name="ic_input_extract_action_done" />
+ <java-symbol type="drawable" name="ic_input_extract_action_previous" />
+ <java-symbol type="drawable" name="ic_input_extract_action_return" />
+
+ <java-symbol type="fraction" name="input_extract_layout_height" />
+ <java-symbol type="fraction" name="input_extract_layout_padding_left" />
+ <java-symbol type="fraction" name="input_extract_layout_padding_left_no_action" />
+ <java-symbol type="fraction" name="input_extract_layout_padding_right" />
+ <java-symbol type="fraction" name="input_extract_text_margin_bottom" />
+ <java-symbol type="fraction" name="input_extract_action_margin_bottom" />
+
+ <java-symbol type="dimen" name="input_extract_action_button_width" />
+ <java-symbol type="dimen" name="input_extract_action_button_height" />
</resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 6611eb1..2ea5c5e 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -890,6 +890,11 @@
<item name="listDivider">@null</item>
<item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
+
+ <item name="windowFixedWidthMajor">@null</item>
+ <item name="windowFixedWidthMinor">@null</item>
+ <item name="windowFixedHeightMajor">@null</item>
+ <item name="windowFixedHeightMinor">@null</item>
</style>
<!-- Theme overlay that overrides window properties to display as a date picker dialog. -->
diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml
index 478d66c..25a6e00 100644
--- a/core/res/res/values/themes_micro.xml
+++ b/core/res/res/values/themes_micro.xml
@@ -83,4 +83,18 @@
<item name="fontFamily">sans-serif-condensed-light</item>
<item name="textColor">@color/micro_text_light</item>
</style>
+
+ <style name="Theme.Micro.Panel" parent="Theme.Material.Panel" />
+ <style name="Theme.Micro.Light.Panel" parent="Theme.Material.Light.Panel" />
+
+ <!-- Default theme for material style input methods, which is used by the
+ {@link android.inputmethodservice.InputMethodService} class.
+ This inherits from Theme.Panel, but sets up IME appropriate animations
+ and a few custom attributes. -->
+ <style name="Theme.Micro.InputMethod" parent="Theme.Micro.Panel">
+ <item name="windowAnimationStyle">@style/Animation.InputMethod</item>
+ <item name="imeFullscreenBackground">#1e282c</item>
+ <item name="imeExtractEnterAnimation">@anim/input_method_extract_enter</item>
+ <item name="imeExtractExitAnimation">@anim/input_method_extract_exit</item>
+ </style>
</resources>
diff --git a/core/res/res/xml/eri.xml b/core/res/res/xml/eri.xml
deleted file mode 100644
index cd66f14..0000000
--- a/core/res/res/xml/eri.xml
+++ /dev/null
@@ -1,118 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** 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.
-*/
--->
-
-<!-- Note that IconMode can be only 0, ON or 1, FLASHING
- The icon is turned OFF if then IconIndex = 1 -->
-
-<EriFile VersionNumber="1357"
- NumberOfEriEntries="12"
- EriFileType="1">
-
- <CallPromptId Id="0"
- CallPromptText="CallPromptId0"/>
-
- <CallPromptId Id="1"
- CallPromptText="CallPromptId1"/>
-
- <CallPromptId Id="2"
- CallPromptText="CallPromptId2"/>
-
- <EriInfo RoamingIndicator="64"
- IconIndex="1"
- IconMode="0"
- EriText="T-CDMA 64"
- CallPromptId="0"
- AlertId="0"/>
-
- <EriInfo RoamingIndicator="65"
- IconIndex="65"
- IconMode="0"
- EriText="T-CDMA 65"
- CallPromptId="0"
- AlertId="0"/>
-
- <EriInfo RoamingIndicator="66"
- IconIndex="1"
- IconMode="0"
- EriText="T-CDMA Ext 66"
- CallPromptId="0"
- AlertId="0"/>
-
- <EriInfo RoamingIndicator="67"
- IconIndex="67"
- IconMode="0"
- EriText="T-CDMA Ext 67"
- CallPromptId="0"
- AlertId="0"/>
-
- <EriInfo RoamingIndicator="68"
- IconIndex="68"
- IconMode="0"
- EriText="T-CDMA Roam 68"
- CallPromptId="0"
- AlertId="0"/>
-
- <EriInfo RoamingIndicator="69"
- IconIndex="69"
- IconMode="1"
- EriText="T-CDMA Ext 69"
- CallPromptId="0"
- AlertId="0"/>
-
- <EriInfo RoamingIndicator="70"
- IconIndex="70"
- IconMode="1"
- EriText="T-CDMA Roam 70"
- CallPromptId="0"
- AlertId="0"/>
-
- <EriInfo RoamingIndicator="71"
- IconIndex="1"
- IconMode="0"
- EriText="T-CDMA Ext 71"
- CallPromptId="0"
- AlertId="0"/>
-
- <EriInfo RoamingIndicator="72"
- IconIndex="72"
- IconMode="0"
- EriText="T-CDMA Ext 72"
- CallPromptId="0"
- AlertId="0"/>
-
- <EriInfo RoamingIndicator="73"
- IconIndex="73"
- IconMode="0"
- EriText="T-CDMA Roam 73"
- CallPromptId="0"
- AlertId="0"/>
-
- <EriInfo RoamingIndicator="74"
- IconIndex="74"
- IconMode="1"
- EriText="T-CDMA Ext 74"
- CallPromptId="0"
- AlertId="0"/>
-
- <EriInfo RoamingIndicator="75"
- IconIndex="75"
- IconMode="1"
- EriText="T-CDMA Roam 75"
- CallPromptId="0"
- AlertId="0"/>
-
-</EriFile>
diff --git a/core/tests/coretests/src/android/util/PatternsTest.java b/core/tests/coretests/src/android/util/PatternsTest.java
index 348f8fd..edb3082 100644
--- a/core/tests/coretests/src/android/util/PatternsTest.java
+++ b/core/tests/coretests/src/android/util/PatternsTest.java
@@ -419,6 +419,36 @@
Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
}
+ @SmallTest
+ public void testAutoLinkWebUrl_doesNotMatchUnicodeSpaces() throws Exception {
+ String part1 = "http://and";
+ String part2 = "roid";
+ String[] emptySpaces = new String[]{
+ "\u00A0", // no-break space
+ "\u2000", // en quad
+ "\u2001", // em quad
+ "\u2002", // en space
+ "\u2003", // em space
+ "\u2004", // three-per-em space
+ "\u2005", // four-per-em space
+ "\u2006", // six-per-em space
+ "\u2007", // figure space
+ "\u2008", // punctuation space
+ "\u2009", // thin space
+ "\u200A", // hair space
+ "\u2028", // line separator
+ "\u2029", // paragraph separator
+ "\u202F", // narrow no-break space
+ "\u3000" // ideographic space
+ };
+
+ for (String emptySpace : emptySpaces) {
+ String url = part1 + emptySpace + part2;
+ assertFalse("Should not match empty space - code:" + emptySpace.codePointAt(0),
+ Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+ }
+ }
+
// Tests for Patterns.IP_ADDRESS
@SmallTest
diff --git a/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java
new file mode 100644
index 0000000..fd57baa
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app.procstats;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+
+import android.os.BatteryStats;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.mockito.Mockito;
+
+/**
+ * Provides test cases for SparseMappingTable.
+ */
+public class SparseMappingTableTest extends TestCase {
+ private static final String TAG = "SparseMappingTableTest";
+
+ final byte ID1 = 1;
+ final byte ID2 = 2;
+
+ final long VALUE1 = 100;
+ final long VALUE2 = 10000000000L;
+
+ /**
+ * Test the parceling and unparceling logic when there is no data.
+ */
+ @SmallTest
+ public void testParcelingEmpty() throws Exception {
+ final SparseMappingTable data = new SparseMappingTable();
+ final SparseMappingTable.Table table = new SparseMappingTable.Table(data);
+
+ final Parcel dataParcel = Parcel.obtain();
+ data.writeToParcel(dataParcel);
+
+ final Parcel tableParcel = Parcel.obtain();
+ table.writeToParcel(tableParcel);
+
+ dataParcel.setDataPosition(0);
+ final SparseMappingTable data1 = new SparseMappingTable();
+ data1.readFromParcel(dataParcel);
+ Assert.assertEquals(0, dataParcel.dataAvail());
+ dataParcel.recycle();
+
+ tableParcel.setDataPosition(0);
+ final SparseMappingTable.Table table1 = new SparseMappingTable.Table(data1);
+ table1.readFromParcel(tableParcel);
+ Assert.assertEquals(0, tableParcel.dataAvail());
+ tableParcel.recycle();
+ }
+
+ /**
+ * Test the parceling and unparceling logic.
+ */
+ @SmallTest
+ public void testParceling() throws Exception {
+ int key;
+ final SparseMappingTable data = new SparseMappingTable();
+ final SparseMappingTable.Table table = new SparseMappingTable.Table(data);
+
+ key = table.getOrAddKey(ID1, 1);
+ table.setValue(key, VALUE1);
+
+ key = table.getOrAddKey(ID2, 1);
+ table.setValue(key, VALUE2);
+
+ final Parcel dataParcel = Parcel.obtain();
+ data.writeToParcel(dataParcel);
+
+ final Parcel tableParcel = Parcel.obtain();
+ table.writeToParcel(tableParcel);
+
+ dataParcel.setDataPosition(0);
+ final SparseMappingTable data1 = new SparseMappingTable();
+ data1.readFromParcel(dataParcel);
+ Assert.assertEquals(0, dataParcel.dataAvail());
+ dataParcel.recycle();
+
+ tableParcel.setDataPosition(0);
+ final SparseMappingTable.Table table1 = new SparseMappingTable.Table(data1);
+ table1.readFromParcel(tableParcel);
+ Assert.assertEquals(0, tableParcel.dataAvail());
+ tableParcel.recycle();
+
+ key = table1.getKey(ID1);
+ Assert.assertEquals(VALUE1, table1.getValue(key));
+
+ key = table1.getKey(ID2);
+ Assert.assertEquals(VALUE2, table1.getValue(key));
+ }
+
+
+ /**
+ * Test that after resetting you can still read data, you just get no values.
+ */
+ @SmallTest
+ public void testParcelingWithReset() throws Exception {
+ int key;
+ final SparseMappingTable data = new SparseMappingTable();
+ final SparseMappingTable.Table table = new SparseMappingTable.Table(data);
+
+ key = table.getOrAddKey(ID1, 1);
+ table.setValue(key, VALUE1);
+
+ data.reset();
+ table.resetTable();
+
+ key = table.getOrAddKey(ID2, 1);
+ table.setValue(key, VALUE2);
+
+ Log.d(TAG, "before: " + data.dumpInternalState(true));
+ Log.d(TAG, "before: " + table.dumpInternalState());
+
+ final Parcel dataParcel = Parcel.obtain();
+ data.writeToParcel(dataParcel);
+
+ final Parcel tableParcel = Parcel.obtain();
+ table.writeToParcel(tableParcel);
+
+ dataParcel.setDataPosition(0);
+ final SparseMappingTable data1 = new SparseMappingTable();
+ data1.readFromParcel(dataParcel);
+ Assert.assertEquals(0, dataParcel.dataAvail());
+ dataParcel.recycle();
+
+ tableParcel.setDataPosition(0);
+ final SparseMappingTable.Table table1 = new SparseMappingTable.Table(data1);
+ table1.readFromParcel(tableParcel);
+ Assert.assertEquals(0, tableParcel.dataAvail());
+ tableParcel.recycle();
+
+ key = table1.getKey(ID1);
+ Assert.assertEquals(SparseMappingTable.INVALID_KEY, key);
+
+ key = table1.getKey(ID2);
+ Assert.assertEquals(VALUE2, table1.getValue(key));
+
+ Log.d(TAG, " after: " + data1.dumpInternalState(true));
+ Log.d(TAG, " after: " + table1.dumpInternalState());
+ }
+
+ /**
+ * Test that it fails if you reset the data and not the table.
+ *
+ * Resetting the table and not the data is basically okay. The data in the
+ * SparseMappingTable will be leaked.
+ */
+ @SmallTest
+ public void testResetDataOnlyFails() throws Exception {
+ int key;
+ final SparseMappingTable data = new SparseMappingTable();
+ final SparseMappingTable.Table table = new SparseMappingTable.Table(data);
+
+ key = table.getOrAddKey(ID1, 1);
+ table.setValue(key, VALUE1);
+
+ Assert.assertEquals(VALUE1, table.getValue(key));
+
+ data.reset();
+
+ try {
+ table.getValue(key);
+ throw new Exception("Exception not thrown after mismatched reset calls.");
+ } catch (RuntimeException ex) {
+ // Good
+ }
+ }
+
+ /**
+ * Test that trying to get data that you didn't add fails correctly.
+ */
+ @SmallTest
+ public void testInvalidKey() throws Exception {
+ int key;
+ final SparseMappingTable data = new SparseMappingTable();
+ final SparseMappingTable.Table table = new SparseMappingTable.Table(data);
+
+ key = table.getKey(ID1);
+
+ // The key should be INVALID_KEY
+ Assert.assertEquals(SparseMappingTable.INVALID_KEY, key);
+
+ // If you get the value with getValueForId you get 0.
+ Assert.assertEquals(0, table.getValueForId(ID1));
+ }
+}
+
+
+
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index d412d7c..8a7d39b 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -111,6 +111,20 @@
<group gid="media" />
</permission>
+ <!-- These are permissions that were mapped to gids but we need
+ to keep them here until an upgrade from L to the current
+ version is to be supported. These permissions are built-in
+ and in L were not stored in packages.xml as a result if they
+ are not defined here while parsing packages.xml we would
+ ignore these permissions being granted to apps and not
+ propagate the granted state. From N we are storing the
+ built-in permissions in packages.xml as the saved storage
+ is negligible (one tag with the permission) compared to
+ the fragility as one can remove a built-in permission which
+ no longer needs to be mapped to gids and break grant propagation. -->
+ <permission name="android.permission.READ_EXTERNAL_STORAGE" />
+ <permission name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
<!-- ================================================================== -->
<!-- ================================================================== -->
<!-- ================================================================== -->
diff --git a/docs/html/guide/topics/manifest/application-element.jd b/docs/html/guide/topics/manifest/application-element.jd
index 5600b5c..887b4ea 100644
--- a/docs/html/guide/topics/manifest/application-element.jd
+++ b/docs/html/guide/topics/manifest/application-element.jd
@@ -472,6 +472,8 @@
{@link android.os.StrictMode.VmPolicy.Builder#detectCleartextNetwork() StrictMode.VmPolicy.Builder.detectCleartextNetwork()}.
<p>This attribute was added in API level 23.</p>
+
+<p>This flag is ignored on Android N and above if an Android Network Security Config is present.</p>
</dd>
<dt><a name="vmSafeMode"></a>{@code android:vmSafeMode}</dt>
diff --git a/graphics/java/android/graphics/PixelCopy.java b/graphics/java/android/graphics/PixelCopy.java
new file mode 100644
index 0000000..c599126
--- /dev/null
+++ b/graphics/java/android/graphics/PixelCopy.java
@@ -0,0 +1,104 @@
+package android.graphics;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.view.Surface;
+import android.view.SurfaceView;
+import android.view.ThreadedRenderer;
+
+/**
+ * Provides a mechanisms to issue pixel copy requests to allow for copy
+ * operations from {@link Surface} to {@link Bitmap}
+ *
+ * @hide
+ */
+public final class PixelCopy {
+ /**
+ * Contains the result of a pixel copy request
+ */
+ public static final class Response {
+ /**
+ * Indicates whether or not the copy request completed successfully.
+ * If this is true, then {@link #bitmap} contains the result of the copy.
+ * If this is false, {@link #bitmap} is unmodified from the originally
+ * passed destination.
+ *
+ * For example a request might fail if the source is protected content
+ * so copies are not allowed. Similarly if the source has nothing to
+ * copy from, because either no frames have been produced yet or because
+ * it has already been destroyed, then this will be false.
+ */
+ public boolean success;
+
+ /**
+ * The output bitmap. This is always the same object that was passed
+ * to request() as the 'dest' bitmap. If {@link #success} is true this
+ * contains a copy of the pixels of the source object. If {@link #success}
+ * is false then this is unmodified.
+ */
+ @NonNull
+ public Bitmap bitmap;
+ }
+
+ public interface OnPixelCopyFinished {
+ /**
+ * Callback for when a pixel copy request has completed. This will be called
+ * regardless of whether the copy succeeded or failed.
+ *
+ * @param response Contains the result of the copy request which includes
+ * whether or not the copy was successful.
+ */
+ void onPixelCopyFinished(PixelCopy.Response response);
+ }
+
+ /**
+ * Requests for the display content of a {@link SurfaceView} to be copied
+ * into a provided {@link Bitmap}.
+ *
+ * The contents of the source will be scaled to fit exactly inside the bitmap.
+ * The pixel format of the source buffer will be converted, as part of the copy,
+ * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
+ * in the SurfaceView's Surface will be used as the source of the copy.
+ *
+ * @param source The source from which to copy
+ * @param dest The destination of the copy. The source will be scaled to
+ * match the width, height, and format of this bitmap.
+ * @param listener Callback for when the pixel copy request completes
+ * @param listenerThread The callback will be invoked on this Handler when
+ * the copy is finished.
+ */
+ public static void request(@NonNull SurfaceView source, @NonNull Bitmap dest,
+ @NonNull OnPixelCopyFinished listener, @NonNull Handler listenerThread) {
+ request(source.getHolder().getSurface(), dest, listener, listenerThread);
+ }
+
+ /**
+ * Requests a copy of the pixels from a {@link Surface} to be copied into
+ * a provided {@link Bitmap}.
+ *
+ * The contents of the source will be scaled to fit exactly inside the bitmap.
+ * The pixel format of the source buffer will be converted, as part of the copy,
+ * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
+ * in the Surface will be used as the source of the copy.
+ *
+ * @param source The source from which to copy
+ * @param dest The destination of the copy. The source will be scaled to
+ * match the width, height, and format of this bitmap.
+ * @param listener Callback for when the pixel copy request completes
+ * @param listenerThread The callback will be invoked on this Handler when
+ * the copy is finished.
+ */
+ public static void request(@NonNull Surface source, @NonNull Bitmap dest,
+ @NonNull OnPixelCopyFinished listener, @NonNull Handler listenerThread) {
+ // TODO: Make this actually async and fast and cool and stuff
+ final PixelCopy.Response response = new PixelCopy.Response();
+ response.success = ThreadedRenderer.copySurfaceInto(source, dest);
+ response.bitmap = dest;
+ listenerThread.post(new Runnable() {
+ @Override
+ public void run() {
+ listener.onPixelCopyFinished(response);
+ }
+ });
+ }
+}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index f15aff7..2886f0d 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -19,6 +19,7 @@
import android.content.res.AssetManager;
import android.util.Log;
import android.util.LongSparseArray;
+import android.util.LruCache;
import android.util.SparseArray;
import org.xmlpull.v1.XmlPullParserException;
@@ -63,6 +64,11 @@
private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache =
new LongSparseArray<SparseArray<Typeface>>(3);
+ /**
+ * Cache for Typeface objects dynamically loaded from assets. Currently max size is 16.
+ */
+ private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16);
+
static Typeface sDefaultTypeface;
static Map<String, Typeface> sSystemFontMap;
static FontFamily[] sFallbackFonts;
@@ -176,22 +182,50 @@
/**
* Create a new typeface from the specified font data.
- * @param mgr The application's asset manager
- * @param path The file name of the font data in the assets directory
+ *
+ * @param mgr The application's asset manager
+ * @param path The file name of the font data in the assets directory
* @return The new typeface.
*/
public static Typeface createFromAsset(AssetManager mgr, String path) {
if (sFallbackFonts != null) {
- FontFamily fontFamily = new FontFamily();
- if (fontFamily.addFontFromAsset(mgr, path)) {
- FontFamily[] families = { fontFamily };
- return createFromFamiliesWithDefault(families);
+ synchronized (sDynamicTypefaceCache) {
+ final String key = createAssetUid(mgr, path);
+ Typeface typeface = sDynamicTypefaceCache.get(key);
+ if (typeface != null) return typeface;
+
+ FontFamily fontFamily = new FontFamily();
+ if (fontFamily.addFontFromAsset(mgr, path)) {
+ FontFamily[] families = { fontFamily };
+ typeface = createFromFamiliesWithDefault(families);
+ sDynamicTypefaceCache.put(key, typeface);
+ return typeface;
+ }
}
}
throw new RuntimeException("Font asset not found " + path);
}
/**
+ * Creates a unique id for a given AssetManager and asset path.
+ *
+ * @param mgr AssetManager instance
+ * @param path The path for the asset.
+ * @return Unique id for a given AssetManager and asset path.
+ */
+ private static String createAssetUid(final AssetManager mgr, String path) {
+ final SparseArray<String> pkgs = mgr.getAssignedPackageIdentifiers();
+ final StringBuilder builder = new StringBuilder();
+ final int size = pkgs.size();
+ for (int i = 0; i < size; i++) {
+ builder.append(pkgs.valueAt(i));
+ builder.append("-");
+ }
+ builder.append(path);
+ return builder.toString();
+ }
+
+ /**
* Create a new typeface from the specified font file.
*
* @param path The path to the font data.
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index e75fb98..0e45780 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -534,13 +534,17 @@
public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
- if (mVectorState.mRootGroup != null || mVectorState.mNativeRendererRefBase != null) {
+ if (mVectorState.mRootGroup != null || mVectorState.mNativeTree != null) {
// This VD has been used to display other VD resource content, clean up.
- mVectorState.mRootGroup = new VGroup();
- if (mVectorState.mNativeRendererRefBase != null) {
- mVectorState.mNativeRendererRefBase.release();
+ if (mVectorState.mRootGroup != null) {
+ // Remove child nodes' reference to tree
+ mVectorState.mRootGroup.setTree(null);
}
- mVectorState.createNativeRenderer(mVectorState.mRootGroup.mNativePtr);
+ mVectorState.mRootGroup = new VGroup();
+ if (mVectorState.mNativeTree != null) {
+ mVectorState.mNativeTree.release();
+ }
+ mVectorState.createNativeTree(mVectorState.mRootGroup);
}
final VectorDrawableState state = mVectorState;
state.setDensity(Drawable.resolveDensity(r, 0));
@@ -734,7 +738,7 @@
Insets mOpticalInsets = Insets.NONE;
String mRootName = null;
VGroup mRootGroup;
- VirtualRefBasePtr mNativeRendererRefBase = null;
+ VirtualRefBasePtr mNativeTree = null;
int mDensity = DisplayMetrics.DENSITY_DEFAULT;
final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<>();
@@ -755,7 +759,7 @@
mTintMode = copy.mTintMode;
mAutoMirrored = copy.mAutoMirrored;
mRootGroup = new VGroup(copy.mRootGroup, mVGTargetsMap);
- createNativeRenderer(mRootGroup.mNativePtr);
+ createNativeTree(mRootGroup);
mBaseWidth = copy.mBaseWidth;
mBaseHeight = copy.mBaseHeight;
@@ -770,15 +774,16 @@
}
}
- private void createNativeRenderer(long rootGroupPtr) {
- mNativeRendererRefBase = new VirtualRefBasePtr(nCreateRenderer(rootGroupPtr));
+ private void createNativeTree(VGroup rootGroup) {
+ mNativeTree = new VirtualRefBasePtr(nCreateTree(rootGroup.mNativePtr));
+ mRootGroup.setTree(mNativeTree);
}
long getNativeRenderer() {
- if (mNativeRendererRefBase == null) {
+ if (mNativeTree == null) {
return 0;
}
- return mNativeRendererRefBase.get();
+ return mNativeTree.get();
}
public boolean canReuseCache() {
@@ -817,7 +822,7 @@
public VectorDrawableState() {
mRootGroup = new VGroup();
- createNativeRenderer(mRootGroup.mNativePtr);
+ createNativeTree(mRootGroup);
}
@Override
@@ -881,16 +886,16 @@
* has changed.
*/
public boolean setAlpha(float alpha) {
- return nSetRootAlpha(mNativeRendererRefBase.get(), alpha);
+ return nSetRootAlpha(mNativeTree.get(), alpha);
}
@SuppressWarnings("unused")
public float getAlpha() {
- return nGetRootAlpha(mNativeRendererRefBase.get());
+ return nGetRootAlpha(mNativeTree.get());
}
}
- static class VGroup implements VObject {
+ static class VGroup extends VObject {
private static final int ROTATE_INDEX = 0;
private static final int PIVOT_X_INDEX = 1;
private static final int PIVOT_Y_INDEX = 2;
@@ -984,11 +989,18 @@
public void addChild(VObject child) {
nAddChild(mNativePtr, child.getNativePtr());
mChildren.add(child);
-
mIsStateful |= child.isStateful();
}
@Override
+ public void setTree(VirtualRefBasePtr treeRoot) {
+ super.setTree(treeRoot);
+ for (int i = 0; i < mChildren.size(); i++) {
+ mChildren.get(i).setTree(treeRoot);
+ }
+ }
+
+ @Override
public long getNativePtr() {
return mNativePtr;
}
@@ -1101,79 +1113,93 @@
/* Setters and Getters, used by animator from AnimatedVectorDrawable. */
@SuppressWarnings("unused")
public float getRotation() {
- return nGetRotation(mNativePtr);
+ return isTreeValid() ? nGetRotation(mNativePtr) : 0;
}
@SuppressWarnings("unused")
public void setRotation(float rotation) {
- nSetRotation(mNativePtr, rotation);
+ if (isTreeValid()) {
+ nSetRotation(mNativePtr, rotation);
+ }
}
@SuppressWarnings("unused")
public float getPivotX() {
- return nGetPivotX(mNativePtr);
+ return isTreeValid() ? nGetPivotX(mNativePtr) : 0;
}
@SuppressWarnings("unused")
public void setPivotX(float pivotX) {
- nSetPivotX(mNativePtr, pivotX);
+ if (isTreeValid()) {
+ nSetPivotX(mNativePtr, pivotX);
+ }
}
@SuppressWarnings("unused")
public float getPivotY() {
- return nGetPivotY(mNativePtr);
+ return isTreeValid() ? nGetPivotY(mNativePtr) : 0;
}
@SuppressWarnings("unused")
public void setPivotY(float pivotY) {
- nSetPivotY(mNativePtr, pivotY);
+ if (isTreeValid()) {
+ nSetPivotY(mNativePtr, pivotY);
+ }
}
@SuppressWarnings("unused")
public float getScaleX() {
- return nGetScaleX(mNativePtr);
+ return isTreeValid() ? nGetScaleX(mNativePtr) : 0;
}
@SuppressWarnings("unused")
public void setScaleX(float scaleX) {
- nSetScaleX(mNativePtr, scaleX);
+ if (isTreeValid()) {
+ nSetScaleX(mNativePtr, scaleX);
+ }
}
@SuppressWarnings("unused")
public float getScaleY() {
- return nGetScaleY(mNativePtr);
+ return isTreeValid() ? nGetScaleY(mNativePtr) : 0;
}
@SuppressWarnings("unused")
public void setScaleY(float scaleY) {
- nSetScaleY(mNativePtr, scaleY);
+ if (isTreeValid()) {
+ nSetScaleY(mNativePtr, scaleY);
+ }
}
@SuppressWarnings("unused")
public float getTranslateX() {
- return nGetTranslateX(mNativePtr);
+ return isTreeValid() ? nGetTranslateX(mNativePtr) : 0;
}
@SuppressWarnings("unused")
public void setTranslateX(float translateX) {
- nSetTranslateX(mNativePtr, translateX);
+ if (isTreeValid()) {
+ nSetTranslateX(mNativePtr, translateX);
+ }
}
@SuppressWarnings("unused")
public float getTranslateY() {
- return nGetTranslateY(mNativePtr);
+ return isTreeValid() ? nGetTranslateY(mNativePtr) : 0;
}
@SuppressWarnings("unused")
public void setTranslateY(float translateY) {
- nSetTranslateY(mNativePtr, translateY);
+ if (isTreeValid()) {
+ nSetTranslateY(mNativePtr, translateY);
+ }
}
}
/**
* Common Path information for clip path and normal path.
*/
- static abstract class VPath implements VObject {
+ static abstract class VPath extends VObject {
protected PathParser.PathData mPathData = null;
String mPathName;
@@ -1203,7 +1229,9 @@
@SuppressWarnings("unused")
public void setPathData(PathParser.PathData pathData) {
mPathData.setPathData(pathData);
- nSetPathData(getNativePtr(), mPathData.getNativePtr());
+ if (isTreeValid()) {
+ nSetPathData(getNativePtr(), mPathData.getNativePtr());
+ }
}
}
@@ -1549,97 +1577,120 @@
/* Setters and Getters, used by animator from AnimatedVectorDrawable. */
@SuppressWarnings("unused")
int getStrokeColor() {
- return nGetStrokeColor(mNativePtr);
+ return isTreeValid() ? nGetStrokeColor(mNativePtr) : 0;
}
@SuppressWarnings("unused")
void setStrokeColor(int strokeColor) {
mStrokeColors = null;
- nSetStrokeColor(mNativePtr, strokeColor);
+ if (isTreeValid()) {
+ nSetStrokeColor(mNativePtr, strokeColor);
+ }
}
@SuppressWarnings("unused")
float getStrokeWidth() {
- return nGetStrokeWidth(mNativePtr);
+ return isTreeValid() ? nGetStrokeWidth(mNativePtr) : 0;
}
@SuppressWarnings("unused")
void setStrokeWidth(float strokeWidth) {
- nSetStrokeWidth(mNativePtr, strokeWidth);
+ if (isTreeValid()) {
+ nSetStrokeWidth(mNativePtr, strokeWidth);
+ }
}
@SuppressWarnings("unused")
float getStrokeAlpha() {
- return nGetStrokeAlpha(mNativePtr);
+ return isTreeValid() ? nGetStrokeAlpha(mNativePtr) : 0;
}
@SuppressWarnings("unused")
void setStrokeAlpha(float strokeAlpha) {
- nSetStrokeAlpha(mNativePtr, strokeAlpha);
+ if (isTreeValid()) {
+ nSetStrokeAlpha(mNativePtr, strokeAlpha);
+ }
}
@SuppressWarnings("unused")
int getFillColor() {
- return nGetFillColor(mNativePtr);
+ return isTreeValid() ? nGetFillColor(mNativePtr) : 0;
}
@SuppressWarnings("unused")
void setFillColor(int fillColor) {
mFillColors = null;
- nSetFillColor(mNativePtr, fillColor);
+ if (isTreeValid()) {
+ nSetFillColor(mNativePtr, fillColor);
+ }
}
@SuppressWarnings("unused")
float getFillAlpha() {
- return nGetFillAlpha(mNativePtr);
+ return isTreeValid() ? nGetFillAlpha(mNativePtr) : 0;
}
@SuppressWarnings("unused")
void setFillAlpha(float fillAlpha) {
- nSetFillAlpha(mNativePtr, fillAlpha);
+ if (isTreeValid()) {
+ nSetFillAlpha(mNativePtr, fillAlpha);
+ }
}
@SuppressWarnings("unused")
float getTrimPathStart() {
- return nGetTrimPathStart(mNativePtr);
+ return isTreeValid() ? nGetTrimPathStart(mNativePtr) : 0;
}
@SuppressWarnings("unused")
void setTrimPathStart(float trimPathStart) {
- nSetTrimPathStart(mNativePtr, trimPathStart);
+ if (isTreeValid()) {
+ nSetTrimPathStart(mNativePtr, trimPathStart);
+ }
}
@SuppressWarnings("unused")
float getTrimPathEnd() {
- return nGetTrimPathEnd(mNativePtr);
+ return isTreeValid() ? nGetTrimPathEnd(mNativePtr) : 0;
}
@SuppressWarnings("unused")
void setTrimPathEnd(float trimPathEnd) {
- nSetTrimPathEnd(mNativePtr, trimPathEnd);
+ if (isTreeValid()) {
+ nSetTrimPathEnd(mNativePtr, trimPathEnd);
+ }
}
@SuppressWarnings("unused")
float getTrimPathOffset() {
- return nGetTrimPathOffset(mNativePtr);
+ return isTreeValid() ? nGetTrimPathOffset(mNativePtr) : 0;
}
@SuppressWarnings("unused")
void setTrimPathOffset(float trimPathOffset) {
- nSetTrimPathOffset(mNativePtr, trimPathOffset);
+ if (isTreeValid()) {
+ nSetTrimPathOffset(mNativePtr, trimPathOffset);
+ }
}
}
- interface VObject {
- long getNativePtr();
- void inflate(Resources r, AttributeSet attrs, Theme theme);
- boolean canApplyTheme();
- void applyTheme(Theme t);
- boolean onStateChange(int[] state);
- boolean isStateful();
+ abstract static class VObject {
+ VirtualRefBasePtr mTreePtr = null;
+ boolean isTreeValid() {
+ return mTreePtr != null && mTreePtr.get() != 0;
+ }
+ void setTree(VirtualRefBasePtr ptr) {
+ mTreePtr = ptr;
+ }
+ abstract long getNativePtr();
+ abstract void inflate(Resources r, AttributeSet attrs, Theme theme);
+ abstract boolean canApplyTheme();
+ abstract void applyTheme(Theme t);
+ abstract boolean onStateChange(int[] state);
+ abstract boolean isStateful();
}
- private static native long nCreateRenderer(long rootGroupPtr);
+ private static native long nCreateTree(long rootGroupPtr);
private static native void nSetRendererViewportSize(long rendererPtr, float viewportWidth,
float viewportHeight);
private static native boolean nSetRootAlpha(long rendererPtr, float alpha);
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 0606b0b..717a1e6 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -84,6 +84,7 @@
Properties.cpp \
PropertyValuesHolder.cpp \
PropertyValuesAnimatorSet.cpp \
+ Readback.cpp \
RenderBufferCache.cpp \
RenderNode.cpp \
RenderProperties.cpp \
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index e209e2a..aba5d4b 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -137,6 +137,9 @@
// whether children with non-zero Z in the chunk should be reordered
bool reorderChildren;
+#if HWUI_NEW_OPS
+ const ClipBase* reorderClip;
+#endif
};
DisplayList();
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index f12e523..6fc74a5 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -269,7 +269,8 @@
}
template <typename V>
-void FrameBuilder::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
+void FrameBuilder::defer3dChildren(const ClipBase* reorderClip, ChildrenSelectMode mode,
+ const V& zTranslatedNodes) {
const int size = zTranslatedNodes.size();
if (size == 0
|| (mode == ChildrenSelectMode::Negative&& zTranslatedNodes[0].key > 0.0f)
@@ -305,7 +306,7 @@
// attempt to render the shadow if the caster about to be drawn is its caster,
// OR if its caster's Z value is similar to the previous potential caster
if (shadowIndex == drawIndex || casterZ - lastCasterZ < 0.1f) {
- deferShadow(*casterNodeOp);
+ deferShadow(reorderClip, *casterNodeOp);
lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
shadowIndex++;
@@ -319,7 +320,7 @@
}
}
-void FrameBuilder::deferShadow(const RenderNodeOp& casterNodeOp) {
+void FrameBuilder::deferShadow(const ClipBase* reorderClip, const RenderNodeOp& casterNodeOp) {
auto& node = *casterNodeOp.renderNode;
auto& properties = node.properties();
@@ -365,6 +366,10 @@
casterPath = frameAllocatedPath;
}
+ // apply reorder clip to shadow, so it respects clip at beginning of reorderable chunk
+ int restoreTo = mCanvasState.save(SaveFlags::MatrixClip);
+ mCanvasState.writableSnapshot()->applyClip(reorderClip,
+ *mCanvasState.currentSnapshot()->transform);
if (CC_LIKELY(!mCanvasState.getRenderTargetClipBounds().isEmpty())) {
Matrix4 shadowMatrixXY(casterNodeOp.localMatrix);
Matrix4 shadowMatrixZ(casterNodeOp.localMatrix);
@@ -386,6 +391,7 @@
currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Shadow);
}
}
+ mCanvasState.restoreToCount(restoreTo);
}
void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) {
@@ -438,11 +444,11 @@
// can't be null, since DL=null node rejection happens before deferNodePropsAndOps
const DisplayList& displayList = *(renderNode.getDisplayList());
- for (const DisplayList::Chunk& chunk : displayList.getChunks()) {
+ for (auto& chunk : displayList.getChunks()) {
FatVector<ZRenderNodeOpPair, 16> zTranslatedNodes;
buildZSortedChildList(&zTranslatedNodes, displayList, chunk);
- defer3dChildren(ChildrenSelectMode::Negative, zTranslatedNodes);
+ defer3dChildren(chunk.reorderClip, ChildrenSelectMode::Negative, zTranslatedNodes);
for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
const RecordedOp* op = displayList.getOps()[opIndex];
receivers[op->opId](*this, *op);
@@ -453,7 +459,7 @@
deferProjectedChildren(renderNode);
}
}
- defer3dChildren(ChildrenSelectMode::Positive, zTranslatedNodes);
+ defer3dChildren(chunk.reorderClip, ChildrenSelectMode::Positive, zTranslatedNodes);
}
}
diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h
index 039ab6b..a6fd761 100644
--- a/libs/hwui/FrameBuilder.h
+++ b/libs/hwui/FrameBuilder.h
@@ -193,9 +193,10 @@
void deferNodePropsAndOps(RenderNode& node);
template <typename V>
- void defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes);
+ void defer3dChildren(const ClipBase* reorderClip, ChildrenSelectMode mode,
+ const V& zTranslatedNodes);
- void deferShadow(const RenderNodeOp& casterOp);
+ void deferShadow(const ClipBase* reorderClip, const RenderNodeOp& casterOp);
void deferProjectedChildren(const RenderNode& renderNode);
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index d1ff670..9d4eccc 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -285,6 +285,7 @@
void JankTracker::reset() {
mData->jankTypeCounts.fill(0);
mData->frameCounts.fill(0);
+ mData->slowFrameCounts.fill(0);
mData->totalFrameCount = 0;
mData->jankFrameCount = 0;
mData->statStartTime = systemTime(CLOCK_MONOTONIC);
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 8f914ac..a8ace8c 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -19,6 +19,7 @@
#include <SkColor.h>
#include <SkPaint.h>
#include <SkPath.h>
+#include <SkPathEffect.h>
#include <SkRect.h>
#include <utils/JenkinsHash.h>
@@ -35,18 +36,34 @@
namespace android {
namespace uirenderer {
+template <class T>
+static bool compareWidthHeight(const T& lhs, const T& rhs) {
+ return (lhs.mWidth == rhs.mWidth) && (lhs.mHeight == rhs.mHeight);
+}
+
+static bool compareRoundRects(const PathDescription::Shape::RoundRect& lhs,
+ const PathDescription::Shape::RoundRect& rhs) {
+ return compareWidthHeight(lhs, rhs) && lhs.mRx == rhs.mRx && lhs.mRy == rhs.mRy;
+}
+
+static bool compareArcs(const PathDescription::Shape::Arc& lhs, const PathDescription::Shape::Arc& rhs) {
+ return compareWidthHeight(lhs, rhs) && lhs.mStartAngle == rhs.mStartAngle &&
+ lhs.mSweepAngle == rhs.mSweepAngle && lhs.mUseCenter == rhs.mUseCenter;
+}
+
///////////////////////////////////////////////////////////////////////////////
// Cache entries
///////////////////////////////////////////////////////////////////////////////
PathDescription::PathDescription()
- : type(kShapeNone)
+ : type(ShapeType::None)
, join(SkPaint::kDefault_Join)
, cap(SkPaint::kDefault_Cap)
, style(SkPaint::kFill_Style)
, miter(4.0f)
, strokeWidth(1.0f)
, pathEffect(nullptr) {
+ // Shape bits should be set to zeroes, because they are used for hash calculation.
memset(&shape, 0, sizeof(Shape));
}
@@ -58,11 +75,12 @@
, miter(paint->getStrokeMiter())
, strokeWidth(paint->getStrokeWidth())
, pathEffect(paint->getPathEffect()) {
+ // Shape bits should be set to zeroes, because they are used for hash calculation.
memset(&shape, 0, sizeof(Shape));
}
hash_t PathDescription::hash() const {
- uint32_t hash = JenkinsHashMix(0, type);
+ uint32_t hash = JenkinsHashMix(0, static_cast<int>(type));
hash = JenkinsHashMix(hash, join);
hash = JenkinsHashMix(hash, cap);
hash = JenkinsHashMix(hash, style);
@@ -73,6 +91,32 @@
return JenkinsHashWhiten(hash);
}
+bool PathDescription::operator==(const PathDescription& rhs) const {
+ if (type != rhs.type) return false;
+ if (join != rhs.join) return false;
+ if (cap != rhs.cap) return false;
+ if (style != rhs.style) return false;
+ if (miter != rhs.miter) return false;
+ if (strokeWidth != rhs.strokeWidth) return false;
+ if (pathEffect != rhs.pathEffect) return false;
+ switch (type) {
+ case ShapeType::None:
+ return 0;
+ case ShapeType::Rect:
+ return compareWidthHeight(shape.rect, rhs.shape.rect);
+ case ShapeType::RoundRect:
+ return compareRoundRects(shape.roundRect, rhs.shape.roundRect);
+ case ShapeType::Circle:
+ return shape.circle.mRadius == rhs.shape.circle.mRadius;
+ case ShapeType::Oval:
+ return compareWidthHeight(shape.oval, rhs.shape.oval);
+ case ShapeType::Arc:
+ return compareArcs(shape.arc, rhs.shape.arc);
+ case ShapeType::Path:
+ return shape.path.mGenerationID == rhs.shape.path.mGenerationID;
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
// Utilities
///////////////////////////////////////////////////////////////////////////////
@@ -322,7 +366,7 @@
LruCache<PathDescription, PathTexture*>::Iterator iter(mCache);
while (iter.next()) {
const PathDescription& key = iter.key();
- if (key.type == kShapePath && key.shape.path.mGenerationID == generationID) {
+ if (key.type == ShapeType::Path && key.shape.path.mGenerationID == generationID) {
pathsToRemove.push(key);
}
}
@@ -336,7 +380,7 @@
}
PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) {
- PathDescription entry(kShapePath, paint);
+ PathDescription entry(ShapeType::Path, paint);
entry.shape.path.mGenerationID = path->getGenerationID();
PathTexture* texture = mCache.get(entry);
@@ -366,9 +410,8 @@
return texture;
}
-void PathCache::remove(const SkPath* path, const SkPaint* paint)
-{
- PathDescription entry(kShapePath, paint);
+void PathCache::remove(const SkPath* path, const SkPaint* paint) {
+ PathDescription entry(ShapeType::Path, paint);
entry.shape.path.mGenerationID = path->getGenerationID();
mCache.remove(entry);
}
@@ -378,7 +421,7 @@
return;
}
- PathDescription entry(kShapePath, paint);
+ PathDescription entry(ShapeType::Path, paint);
entry.shape.path.mGenerationID = path->getGenerationID();
PathTexture* texture = mCache.get(entry);
@@ -417,7 +460,7 @@
PathTexture* PathCache::getRoundRect(float width, float height,
float rx, float ry, const SkPaint* paint) {
- PathDescription entry(kShapeRoundRect, paint);
+ PathDescription entry(ShapeType::RoundRect, paint);
entry.shape.roundRect.mWidth = width;
entry.shape.roundRect.mHeight = height;
entry.shape.roundRect.mRx = rx;
@@ -442,7 +485,7 @@
///////////////////////////////////////////////////////////////////////////////
PathTexture* PathCache::getCircle(float radius, const SkPaint* paint) {
- PathDescription entry(kShapeCircle, paint);
+ PathDescription entry(ShapeType::Circle, paint);
entry.shape.circle.mRadius = radius;
PathTexture* texture = get(entry);
@@ -462,7 +505,7 @@
///////////////////////////////////////////////////////////////////////////////
PathTexture* PathCache::getOval(float width, float height, const SkPaint* paint) {
- PathDescription entry(kShapeOval, paint);
+ PathDescription entry(ShapeType::Oval, paint);
entry.shape.oval.mWidth = width;
entry.shape.oval.mHeight = height;
@@ -485,7 +528,7 @@
///////////////////////////////////////////////////////////////////////////////
PathTexture* PathCache::getRect(float width, float height, const SkPaint* paint) {
- PathDescription entry(kShapeRect, paint);
+ PathDescription entry(ShapeType::Rect, paint);
entry.shape.rect.mWidth = width;
entry.shape.rect.mHeight = height;
@@ -509,7 +552,7 @@
PathTexture* PathCache::getArc(float width, float height,
float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) {
- PathDescription entry(kShapeArc, paint);
+ PathDescription entry(ShapeType::Arc, paint);
entry.shape.arc.mWidth = width;
entry.shape.arc.mHeight = height;
entry.shape.arc.mStartAngle = startAngle;
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index d2633aa..6368ddd 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -25,6 +25,7 @@
#include "utils/Pair.h"
#include <GLES2/gl2.h>
+#include <SkPaint.h>
#include <SkPath.h>
#include <utils/LruCache.h>
#include <utils/Mutex.h>
@@ -108,18 +109,18 @@
sp<Task<SkBitmap*> > mTask;
}; // struct PathTexture
-enum ShapeType {
- kShapeNone,
- kShapeRect,
- kShapeRoundRect,
- kShapeCircle,
- kShapeOval,
- kShapeArc,
- kShapePath
+enum class ShapeType {
+ None,
+ Rect,
+ RoundRect,
+ Circle,
+ Oval,
+ Arc,
+ Path
};
struct PathDescription {
- DESCRIPTION_TYPE(PathDescription);
+ HASHABLE_TYPE(PathDescription);
ShapeType type;
SkPaint::Join join;
SkPaint::Cap cap;
@@ -159,8 +160,6 @@
PathDescription();
PathDescription(ShapeType shapeType, const SkPaint* paint);
-
- hash_t hash() const;
};
/**
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
new file mode 100644
index 0000000..d7df77c
--- /dev/null
+++ b/libs/hwui/Readback.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 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 "Readback.h"
+
+#include "Caches.h"
+#include "Image.h"
+#include "GlopBuilder.h"
+#include "renderstate/RenderState.h"
+#include "renderthread/EglManager.h"
+#include "utils/GLUtils.h"
+
+#include <GLES2/gl2.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace uirenderer {
+
+bool Readback::copySurfaceInto(renderthread::RenderThread& renderThread,
+ Surface& surface, SkBitmap* bitmap) {
+ // TODO: Clean this up and unify it with LayerRenderer::copyLayer,
+ // of which most of this is copied from.
+ renderThread.eglManager().initialize();
+
+ Caches& caches = Caches::getInstance();
+ RenderState& renderState = renderThread.renderState();
+ int destWidth = bitmap->width();
+ int destHeight = bitmap->height();
+ if (destWidth > caches.maxTextureSize
+ || destHeight > caches.maxTextureSize) {
+ ALOGW("Can't copy surface into bitmap, %dx%d exceeds max texture size %d",
+ destWidth, destHeight, caches.maxTextureSize);
+ return false;
+ }
+ GLuint fbo = renderState.createFramebuffer();
+ if (!fbo) {
+ ALOGW("Could not obtain an FBO");
+ return false;
+ }
+
+ SkAutoLockPixels alp(*bitmap);
+
+ GLuint texture;
+
+ GLenum format;
+ GLenum type;
+
+ switch (bitmap->colorType()) {
+ case kAlpha_8_SkColorType:
+ format = GL_ALPHA;
+ type = GL_UNSIGNED_BYTE;
+ break;
+ case kRGB_565_SkColorType:
+ format = GL_RGB;
+ type = GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ case kARGB_4444_SkColorType:
+ format = GL_RGBA;
+ type = GL_UNSIGNED_SHORT_4_4_4_4;
+ break;
+ case kN32_SkColorType:
+ default:
+ format = GL_RGBA;
+ type = GL_UNSIGNED_BYTE;
+ break;
+ }
+
+ renderState.bindFramebuffer(fbo);
+
+ // TODO: Use layerPool or something to get this maybe? But since we
+ // need explicit format control we can't currently.
+
+ // Setup the rendertarget
+ glGenTextures(1, &texture);
+ caches.textureState().activateTexture(0);
+ caches.textureState().bindTexture(texture);
+ glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexImage2D(GL_TEXTURE_2D, 0, format, destWidth, destHeight,
+ 0, format, type, nullptr);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, texture, 0);
+
+ // Setup the source
+ sp<GraphicBuffer> sourceBuffer;
+ sp<Fence> sourceFence;
+ // FIXME: Waiting on an API from libgui for this
+ // surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence);
+ if (!sourceBuffer.get()) {
+ ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
+ return false;
+ }
+ int err = sourceFence->wait(500 /* ms */);
+ if (err != NO_ERROR) {
+ ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
+ return false;
+ }
+ Image sourceImage(sourceBuffer);
+ if (!sourceImage.getTexture()) {
+ ALOGW("Failed to make an EGLImage from the GraphicBuffer");
+ return false;
+ }
+ Texture sourceTexture(caches);
+ sourceTexture.wrap(sourceImage.getTexture(),
+ sourceBuffer->getWidth(), sourceBuffer->getHeight(), 0 /* total lie */);
+
+ {
+ // Draw & readback
+ renderState.setViewport(destWidth, destHeight);
+ renderState.scissor().setEnabled(false);
+ renderState.blend().syncEnabled();
+ renderState.stencil().disable();
+
+ Rect destRect(destWidth, destHeight);
+ Glop glop;
+ GlopBuilder(renderState, caches, &glop)
+ .setRoundRectClipState(nullptr)
+ .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
+ .setFillLayer(sourceTexture, nullptr, 1.0f, SkXfermode::kSrc_Mode,
+ Blend::ModeOrderSwap::NoSwap)
+ .setTransform(Matrix4::identity(), TransformFlags::None)
+ .setModelViewMapUnitToRect(destRect)
+ .build();
+ Matrix4 ortho;
+ ortho.loadOrtho(destWidth, destHeight);
+ renderState.render(glop, ortho);
+
+ glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
+ type, bitmap->getPixels());
+ }
+
+ // Cleanup
+ caches.textureState().deleteTexture(texture);
+ renderState.deleteFramebuffer(fbo);
+
+ GL_CHECKPOINT(MODERATE);
+
+ return true;
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
new file mode 100644
index 0000000..ea03c82
--- /dev/null
+++ b/libs/hwui/Readback.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include "renderthread/RenderThread.h"
+
+#include <SkBitmap.h>
+#include <gui/Surface.h>
+
+namespace android {
+namespace uirenderer {
+
+class Readback {
+public:
+ static bool copySurfaceInto(renderthread::RenderThread& renderThread,
+ Surface& surface, SkBitmap* bitmap);
+};
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index b78497d..ab733f1 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -57,6 +57,16 @@
return displayList;
}
+void RecordingCanvas::insertReorderBarrier(bool enableReorder) {
+ if (enableReorder) {
+ mDeferredBarrierType = DeferredBarrierType::OutOfOrder;
+ mDeferredBarrierClip = getRecordedClip();
+ } else {
+ mDeferredBarrierType = DeferredBarrierType::InOrder;
+ mDeferredBarrierClip = nullptr;
+ }
+}
+
SkCanvas* RecordingCanvas::asSkCanvas() {
LOG_ALWAYS_FATAL_IF(!mDisplayList,
"attempting to get an SkCanvas when we are not recording!");
@@ -610,6 +620,7 @@
newChunk.beginOpIndex = insertIndex;
newChunk.endOpIndex = insertIndex + 1;
newChunk.reorderChildren = (mDeferredBarrierType == DeferredBarrierType::OutOfOrder);
+ newChunk.reorderClip = mDeferredBarrierClip;
int nextChildIndex = mDisplayList->children.size();
newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index acb88e2..219296c 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -55,10 +55,7 @@
// ----------------------------------------------------------------------------
// MISC HWUI OPERATIONS - TODO: CATEGORIZE
// ----------------------------------------------------------------------------
- virtual void insertReorderBarrier(bool enableReorder) override {
- mDeferredBarrierType = enableReorder
- ? DeferredBarrierType::OutOfOrder : DeferredBarrierType::InOrder;
- }
+ virtual void insertReorderBarrier(bool enableReorder) override;
virtual void drawLayer(DeferredLayerUpdater* layerHandle) override;
virtual void drawRenderNode(RenderNode* renderNode) override;
@@ -312,6 +309,7 @@
std::unique_ptr<SkiaCanvasProxy> mSkiaCanvasProxy;
ResourceCache& mResourceCache;
DeferredBarrierType mDeferredBarrierType = DeferredBarrierType::None;
+ const ClipBase* mDeferredBarrierClip = nullptr;
DisplayList* mDisplayList = nullptr;
bool mHighContrastText = false;
SkAutoTUnref<SkDrawFilter> mDrawFilter;
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index 14c8f392..cfdc084 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -35,13 +35,14 @@
///////////////////////////////////////////////////////////////////////////////
TessellationCache::Description::Description()
- : type(kNone)
+ : type(Type::None)
, scaleX(1.0f)
, scaleY(1.0f)
, aa(false)
, cap(SkPaint::kDefault_Cap)
, style(SkPaint::kFill_Style)
, strokeWidth(1.0f) {
+ // Shape bits should be set to zeroes, because they are used for hash calculation.
memset(&shape, 0, sizeof(Shape));
}
@@ -52,11 +53,30 @@
, style(paint.getStyle())
, strokeWidth(paint.getStrokeWidth()) {
PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
+ // Shape bits should be set to zeroes, because they are used for hash calculation.
memset(&shape, 0, sizeof(Shape));
}
+bool TessellationCache::Description::operator==(const TessellationCache::Description& rhs) const {
+ if (type != rhs.type) return false;
+ if (scaleX != rhs.scaleX) return false;
+ if (scaleY != rhs.scaleY) return false;
+ if (aa != rhs.aa) return false;
+ if (cap != rhs.cap) return false;
+ if (style != rhs.style) return false;
+ if (strokeWidth != rhs.strokeWidth) return false;
+ if (type == Type::None) return true;
+ const Shape::RoundRect& lRect = shape.roundRect;
+ const Shape::RoundRect& rRect = rhs.shape.roundRect;
+
+ if (lRect.width != rRect.width) return false;
+ if (lRect.height != rRect.height) return false;
+ if (lRect.rx != rRect.rx) return false;
+ return lRect.ry == rRect.ry;
+}
+
hash_t TessellationCache::Description::hash() const {
- uint32_t hash = JenkinsHashMix(0, type);
+ uint32_t hash = JenkinsHashMix(0, static_cast<int>(type));
hash = JenkinsHashMix(hash, aa);
hash = JenkinsHashMix(hash, cap);
hash = JenkinsHashMix(hash, style);
@@ -77,17 +97,23 @@
TessellationCache::ShadowDescription::ShadowDescription()
: nodeKey(nullptr) {
- memset(&matrixData, 0, 16 * sizeof(float));
+ memset(&matrixData, 0, sizeof(matrixData));
}
-TessellationCache::ShadowDescription::ShadowDescription(const void* nodeKey, const Matrix4* drawTransform)
+TessellationCache::ShadowDescription::ShadowDescription(const SkPath* nodeKey, const Matrix4* drawTransform)
: nodeKey(nodeKey) {
- memcpy(&matrixData, drawTransform->data, 16 * sizeof(float));
+ memcpy(&matrixData, drawTransform->data, sizeof(matrixData));
+}
+
+bool TessellationCache::ShadowDescription::operator==(
+ const TessellationCache::ShadowDescription& rhs) const {
+ return nodeKey == rhs.nodeKey
+ && memcmp(&matrixData, &rhs.matrixData, sizeof(matrixData)) == 0;
}
hash_t TessellationCache::ShadowDescription::hash() const {
uint32_t hash = JenkinsHashMixBytes(0, (uint8_t*) &nodeKey, sizeof(const void*));
- hash = JenkinsHashMixBytes(hash, (uint8_t*) &matrixData, 16 * sizeof(float));
+ hash = JenkinsHashMixBytes(hash, (uint8_t*) &matrixData, sizeof(matrixData));
return JenkinsHashWhiten(hash);
}
@@ -428,7 +454,7 @@
TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(
const Matrix4& transform, const SkPaint& paint,
float width, float height, float rx, float ry) {
- Description entry(Description::kRoundRect, transform, paint);
+ Description entry(Description::Type::RoundRect, transform, paint);
entry.shape.roundRect.width = width;
entry.shape.roundRect.height = height;
entry.shape.roundRect.rx = rx;
diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h
index 0bd6365..6141b4e 100644
--- a/libs/hwui/TessellationCache.h
+++ b/libs/hwui/TessellationCache.h
@@ -52,10 +52,10 @@
typedef Pair<VertexBuffer*, VertexBuffer*> vertexBuffer_pair_t;
struct Description {
- DESCRIPTION_TYPE(Description);
- enum Type {
- kNone,
- kRoundRect,
+ HASHABLE_TYPE(Description);
+ enum class Type {
+ None,
+ RoundRect,
};
Type type;
@@ -76,18 +76,16 @@
Description();
Description(Type type, const Matrix4& transform, const SkPaint& paint);
- hash_t hash() const;
void setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const;
};
struct ShadowDescription {
- DESCRIPTION_TYPE(ShadowDescription);
- const void* nodeKey;
+ HASHABLE_TYPE(ShadowDescription);
+ const SkPath* nodeKey;
float matrixData[16];
ShadowDescription();
- ShadowDescription(const void* nodeKey, const Matrix4* drawTransform);
- hash_t hash() const;
+ ShadowDescription(const SkPath* nodeKey, const Matrix4* drawTransform);
};
class ShadowTask : public Task<vertexBuffer_pair_t> {
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index b39de26..a455f57 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -24,7 +24,8 @@
MinikinFontSkia::MinikinFontSkia(SkTypeface* typeface, const void* fontData, size_t fontSize,
int ttcIndex) :
- mTypeface(typeface), mFontData(fontData), mFontSize(fontSize), mTtcIndex(ttcIndex) {
+ MinikinFont(typeface->uniqueID()), mTypeface(typeface), mFontData(fontData),
+ mFontSize(fontSize), mTtcIndex(ttcIndex) {
}
MinikinFontSkia::~MinikinFontSkia() {
@@ -99,10 +100,6 @@
return mTtcIndex;
}
-int32_t MinikinFontSkia::GetUniqueId() const {
- return mTypeface->uniqueID();
-}
-
uint32_t MinikinFontSkia::packPaintFlags(const SkPaint* paint) {
uint32_t flags = paint->getFlags();
SkPaint::Hinting hinting = paint->getHinting();
diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h
index dbc65f9..a7c9fb0 100644
--- a/libs/hwui/hwui/MinikinSkia.h
+++ b/libs/hwui/hwui/MinikinSkia.h
@@ -41,8 +41,6 @@
const void* GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy);
- int32_t GetUniqueId() const;
-
SkTypeface* GetSkTypeface() const;
// Access to underlying raw font bytes
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 890d4a1..ab66b2a 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -113,11 +113,18 @@
mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
mHaveNewSurface = true;
mSwapHistory.clear();
+ makeCurrent();
} else {
mRenderThread.removeFrameCallback(this);
}
}
+void CanvasContext::requireSurface() {
+ LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+ "requireSurface() called but no surface set!");
+ makeCurrent();
+}
+
void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) {
mSwapBehavior = swapBehavior;
}
@@ -139,18 +146,6 @@
return mRenderThread.removeFrameCallback(this);
}
-void CanvasContext::setStopped(bool stopped) {
- if (mStopped != stopped) {
- mStopped = stopped;
- if (mStopped) {
- mRenderThread.removeFrameCallback(this);
- if (mEglManager.isCurrent(mEglSurface)) {
- mEglManager.makeCurrent(EGL_NO_SURFACE);
- }
- }
- }
-}
-
// TODO: don't pass viewport size, it's automatic via EGL
void CanvasContext::setup(int width, int height, float lightRadius,
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
@@ -177,9 +172,7 @@
mOpaque = opaque;
}
-bool CanvasContext::makeCurrent() {
- if (mStopped) return false;
-
+void CanvasContext::makeCurrent() {
// TODO: Figure out why this workaround is needed, see b/13913604
// In the meantime this matches the behavior of GLRenderer, so it is not a regression
EGLint error = 0;
@@ -187,7 +180,6 @@
if (error) {
setSurface(nullptr);
}
- return !error;
}
static bool wasSkipped(FrameInfo* info) {
@@ -679,7 +671,7 @@
}
Layer* CanvasContext::createTextureLayer() {
- mEglManager.initialize();
+ requireSurface();
return LayerRenderer::createTextureLayer(mRenderThread.renderState());
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 52df3abe..9350114 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -82,14 +82,13 @@
void initialize(Surface* surface);
void updateSurface(Surface* surface);
bool pauseSurface(Surface* surface);
- void setStopped(bool stopped);
bool hasSurface() { return mNativeSurface.get(); }
void setup(int width, int height, float lightRadius,
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
void setLightCenter(const Vector3& lightCenter);
void setOpaque(bool opaque);
- bool makeCurrent();
+ void makeCurrent();
void prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
int64_t syncQueued, RenderNode* target);
void draw();
@@ -173,6 +172,7 @@
friend class android::uirenderer::RenderState;
void setSurface(Surface* window);
+ void requireSurface();
void freePrefetchedLayers(TreeObserver* observer);
@@ -185,7 +185,6 @@
EglManager& mEglManager;
sp<Surface> mNativeSurface;
EGLSurface mEglSurface = EGL_NO_SURFACE;
- bool mStopped = false;
bool mBufferPreserved = false;
SwapBehavior mSwapBehavior = kSwap_default;
struct SwapHistory {
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index ed472ac..651aaa2 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -115,7 +115,7 @@
ATRACE_CALL();
int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
mRenderThread->timeLord().vsyncReceived(vsync);
- bool canDraw = mContext->makeCurrent();
+ mContext->makeCurrent();
Caches::getInstance().textureCache.resetMarkInUse(mContext);
for (size_t i = 0; i < mLayers.size(); i++) {
@@ -126,9 +126,8 @@
// This is after the prepareTree so that any pending operations
// (RenderNode tree state, prefetched layers, etc...) will be flushed.
- if (CC_UNLIKELY(!mContext->hasSurface() || !canDraw)) {
+ if (CC_UNLIKELY(!mContext->hasSurface())) {
mSyncResult |= kSync_LostSurfaceRewardIfFound;
- info.out.canDrawThisFrame = false;
}
if (info.out.hasAnimations) {
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index ac6a28f..8def7ad 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -270,6 +270,12 @@
// Ensure we always have a valid surface & context
surface = mPBufferSurface;
}
+ // TODO: Temporary to help diagnose b/27286867
+ if (mCurrentSurface == mPBufferSurface || surface == mPBufferSurface) {
+ ALOGD("Switching from surface %p%s to %p%s", mCurrentSurface,
+ mCurrentSurface == mPBufferSurface ? " (pbuffer)" : "",
+ surface, surface == mPBufferSurface ? " (pbuffer)" : "");
+ }
if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
if (errOut) {
*errOut = eglGetError();
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index c5a2dc7..096093c 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -19,6 +19,7 @@
#include "DeferredLayerUpdater.h"
#include "DisplayList.h"
#include "LayerRenderer.h"
+#include "Readback.h"
#include "Rect.h"
#include "renderthread/CanvasContext.h"
#include "renderthread/RenderTask.h"
@@ -167,18 +168,6 @@
return (bool) postAndWait(task);
}
-CREATE_BRIDGE2(setStopped, CanvasContext* context, bool stopped) {
- args->context->setStopped(args->stopped);
- return nullptr;
-}
-
-void RenderProxy::setStopped(bool stopped) {
- SETUP_TASK(setStopped);
- args->context = mContext;
- args->stopped = stopped;
- postAndWait(task);
-}
-
CREATE_BRIDGE6(setup, CanvasContext* context, int width, int height,
float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
args->context->setup(args->width, args->height, args->lightRadius,
@@ -616,6 +605,20 @@
post(task);
}
+CREATE_BRIDGE3(copySurfaceInto, RenderThread* thread,
+ Surface* surface, SkBitmap* bitmap) {
+ return (void*) Readback::copySurfaceInto(*args->thread,
+ *args->surface, args->bitmap);
+}
+
+bool RenderProxy::copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap) {
+ SETUP_TASK(copySurfaceInto);
+ args->bitmap = bitmap;
+ args->surface = surface.get();
+ args->thread = &RenderThread::getInstance();
+ return (bool) staticPostAndWait(task);
+}
+
void RenderProxy::post(RenderTask* task) {
mRenderThread.queue(task);
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 32d3283b..98aace0 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -79,7 +79,6 @@
ANDROID_API void initialize(const sp<Surface>& surface);
ANDROID_API void updateSurface(const sp<Surface>& surface);
ANDROID_API bool pauseSurface(const sp<Surface>& surface);
- ANDROID_API void setStopped(bool stopped);
ANDROID_API void setup(int width, int height, float lightRadius,
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
ANDROID_API void setLightCenter(const Vector3& lightCenter);
@@ -127,6 +126,8 @@
ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer);
ANDROID_API long getDroppedFrameReportCount();
+ ANDROID_API static bool copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap);
+
private:
RenderThread& mRenderThread;
CanvasContext* mContext;
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 9877439..209a104 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -1722,6 +1722,35 @@
EXPECT_EQ(4, renderer.getIndex());
}
+RENDERTHREAD_TEST(FrameBuilder, shadowClipping) {
+ class ShadowClippingTestRenderer : public TestRendererBase {
+ public:
+ void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipState->rect)
+ << "Shadow must respect pre-barrier canvas clip value.";
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+ }
+ };
+ auto parent = TestUtils::createNode(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ // Apply a clip before the reorder barrier/shadow casting child is drawn.
+ // This clip must be applied to the shadow cast by the child.
+ canvas.clipRect(25, 25, 75, 75, SkRegion::kIntersect_Op);
+ canvas.insertReorderBarrier(true);
+ canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
+ });
+
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+ TestUtils::createSyncedNodeList(parent),
+ (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
+ ShadowClippingTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(2, renderer.getIndex());
+}
+
static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
class PropertyTestRenderer : public TestRendererBase {
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index c49ff71..18171de 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -603,6 +603,36 @@
EXPECT_TRUE(chunks[1].reorderChildren);
}
+TEST(RecordingCanvas, insertReorderBarrier_clip) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ // first chunk: no recorded clip
+ canvas.insertReorderBarrier(true);
+ canvas.drawRect(0, 0, 400, 400, SkPaint());
+
+ // second chunk: no recorded clip, since inorder region
+ canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
+ canvas.insertReorderBarrier(false);
+ canvas.drawRect(0, 0, 400, 400, SkPaint());
+
+ // third chunk: recorded clip
+ canvas.insertReorderBarrier(true);
+ canvas.drawRect(0, 0, 400, 400, SkPaint());
+ });
+
+ auto chunks = dl->getChunks();
+ ASSERT_EQ(3u, chunks.size());
+
+ EXPECT_TRUE(chunks[0].reorderChildren);
+ EXPECT_EQ(nullptr, chunks[0].reorderClip);
+
+ EXPECT_FALSE(chunks[1].reorderChildren);
+ EXPECT_EQ(nullptr, chunks[1].reorderClip);
+
+ EXPECT_TRUE(chunks[2].reorderChildren);
+ ASSERT_NE(nullptr, chunks[2].reorderClip);
+ EXPECT_EQ(Rect(200, 200), chunks[2].reorderClip->rect);
+}
+
TEST(RecordingCanvas, refPaint) {
SkPaint paint;
diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h
index ccf2287..7212897b 100644
--- a/libs/hwui/utils/Macros.h
+++ b/libs/hwui/utils/Macros.h
@@ -23,12 +23,10 @@
Type(const Type&) = delete; \
void operator=(const Type&) = delete
-#define DESCRIPTION_TYPE(Type) \
- int compare(const Type& rhs) const { return memcmp(this, &rhs, sizeof(Type));} \
- bool operator==(const Type& other) const { return compare(other) == 0; } \
- bool operator!=(const Type& other) const { return compare(other) != 0; } \
- friend inline int strictly_order_type(const Type& lhs, const Type& rhs) { return lhs.compare(rhs) < 0; } \
- friend inline int compare_type(const Type& lhs, const Type& rhs) { return lhs.compare(rhs); } \
+#define HASHABLE_TYPE(Type) \
+ bool operator==(const Type& other) const; \
+ hash_t hash() const; \
+ bool operator!=(const Type& other) const { return !(*this == other); } \
friend inline hash_t hash_type(const Type& entry) { return entry.hash(); }
#define REQUIRE_COMPATIBLE_LAYOUT(Type) \
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index ca306cc..4504e69 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -37,6 +37,8 @@
import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
/**
* The AudioRecord class manages the audio resources for Java applications
* to record audio from the audio input hardware of the platform. This is
@@ -874,22 +876,24 @@
* Calling {@link #startRecording()} following a {@link #stop()} will reset
* the frame count to 0.
*
- * @param timestamp a reference to a non-null AudioTimestamp instance.
+ * @param outTimestamp a caller provided non-null AudioTimestamp instance,
+ * which is updated with the AudioRecord frame delivery information upon success.
* @param timebase one of
* {@link AudioTimestamp#TIMEBASE_BOOTTIME AudioTimestamp.TIMEBASE_BOOTTIME} or
- * {@link AudioTimestamp#TIMEBASE_MONOTONIC AudioTimestamp.TIMEBASE_MONOTONIC}.
+ * {@link AudioTimestamp#TIMEBASE_MONOTONIC AudioTimestamp.TIMEBASE_MONOTONIC},
+ * used to select the clock for the AudioTimestamp time.
* @return {@link #SUCCESS} if a timestamp is available,
* or {@link #ERROR_INVALID_OPERATION} if a timestamp not available.
*/
- public int getTimestamp(@NonNull AudioTimestamp timestamp,
+ public int getTimestamp(@NonNull AudioTimestamp outTimestamp,
@AudioTimestamp.Timebase int timebase)
{
- if (timestamp == null ||
+ if (outTimestamp == null ||
(timebase != AudioTimestamp.TIMEBASE_BOOTTIME
&& timebase != AudioTimestamp.TIMEBASE_MONOTONIC)) {
throw new IllegalArgumentException();
}
- return native_get_timestamp(timestamp, timebase);
+ return native_get_timestamp(outTimestamp, timebase);
}
/**
@@ -1320,6 +1324,7 @@
* Note: The query is only valid if the AudioRecord is currently recording. If it is not,
* <code>getRoutedDevice()</code> will return null.
*/
+ @Override
public AudioDeviceInfo getRoutedDevice() {
int deviceId = native_getRoutedDeviceId();
if (deviceId == 0) {
@@ -1338,8 +1343,8 @@
/*
* Call BEFORE adding a routing callback handler.
*/
- private void testEnableNativeRoutingCallbacks() {
- if (mRoutingChangeListeners.size() == 0 && mNewRoutingChangeListeners.size() == 0) {
+ private void testEnableNativeRoutingCallbacksLocked() {
+ if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback();
}
}
@@ -1347,24 +1352,23 @@
/*
* Call AFTER removing a routing callback handler.
*/
- private void testDisableNativeRoutingCallbacks() {
- if (mRoutingChangeListeners.size() == 0 && mNewRoutingChangeListeners.size() == 0) {
+ private void testDisableNativeRoutingCallbacksLocked() {
+ if (mRoutingChangeListeners.size() == 0) {
native_disableDeviceCallback();
}
}
//--------------------------------------------------------------------------
- // >= "N" (Re)Routing Info
+ // (Re)Routing Info
//--------------------
/**
* The list of AudioRouting.OnRoutingChangedListener interfaces added (with
- * {@link AudioRecord#addOnRoutingListener(AudioRouting.OnRoutingChangedListener,
- * android.os.Handler)}
- * by an app to receive (re)routing notifications.
+ * {@link AudioRecord#addOnRoutingChangedListener} by an app to receive
+ * (re)routing notifications.
*/
- private ArrayMap<AudioRouting.OnRoutingChangedListener, NativeNewRoutingEventHandlerDelegate>
- mNewRoutingChangeListeners =
- new ArrayMap<AudioRouting.OnRoutingChangedListener, NativeNewRoutingEventHandlerDelegate>();
+ @GuardedBy("mRoutingChangeListeners")
+ private ArrayMap<AudioRouting.OnRoutingChangedListener,
+ NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>();
/**
* Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of
@@ -1375,14 +1379,15 @@
* the callback. If <code>null</code>, the {@link Handler} associated with the main
* {@link Looper} will be used.
*/
- public void addOnRoutingListener(AudioRouting.OnRoutingChangedListener listener,
+ @Override
+ public void addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener,
android.os.Handler handler) {
- if (listener != null && !mNewRoutingChangeListeners.containsKey(listener)) {
- synchronized (mNewRoutingChangeListeners) {
- testEnableNativeRoutingCallbacks();
- mNewRoutingChangeListeners.put(
- listener, new NativeNewRoutingEventHandlerDelegate(this, listener,
- handler != null ? handler : new Handler(mInitializationLooper)));
+ synchronized (mRoutingChangeListeners) {
+ if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
+ testEnableNativeRoutingCallbacksLocked();
+ mRoutingChangeListeners.put(
+ listener, new NativeRoutingEventHandlerDelegate(this, listener,
+ handler != null ? handler : new Handler(mInitializationLooper)));
}
}
}
@@ -1393,39 +1398,42 @@
* @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
* to remove.
*/
- public void removeOnRoutingListener(AudioRouting.OnRoutingChangedListener listener) {
- synchronized (mNewRoutingChangeListeners) {
- if (mNewRoutingChangeListeners.containsKey(listener)) {
- mNewRoutingChangeListeners.remove(listener);
- testDisableNativeRoutingCallbacks();
+ @Override
+ public void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener) {
+ synchronized (mRoutingChangeListeners) {
+ if (mRoutingChangeListeners.containsKey(listener)) {
+ mRoutingChangeListeners.remove(listener);
+ testDisableNativeRoutingCallbacksLocked();
}
}
}
//--------------------------------------------------------------------------
- // Marshmallow (Re)Routing Info
+ // (Re)Routing Info
//--------------------
/**
- * Defines the interface by which applications can receive notifications of routing
- * changes for the associated {@link AudioRecord}.
+ * Defines the interface by which applications can receive notifications of
+ * routing changes for the associated {@link AudioRecord}.
+ *
+ * @deprecated users should switch to the general purpose
+ * {@link AudioRouting.OnRoutingChangedListener} class instead.
*/
- public interface OnRoutingChangedListener {
+ @Deprecated
+ public interface OnRoutingChangedListener extends AudioRouting.OnRoutingChangedListener {
/**
- * Called when the routing of an AudioRecord changes from either and explicit or
- * policy rerouting. Use {@link #getRoutedDevice()} to retrieve the newly routed-from
- * device.
+ * Called when the routing of an AudioRecord changes from either and
+ * explicit or policy rerouting. Use {@link #getRoutedDevice()} to
+ * retrieve the newly routed-from device.
*/
public void onRoutingChanged(AudioRecord audioRecord);
- }
- /**
- * The list of AudioRecord.OnRoutingChangedListener interface added (with
- * {@link AudioRecord#addOnRoutingChangedListener(OnRoutingChangedListener,android.os.Handler)}
- * by an app to receive (re)routing notifications.
- */
- private ArrayMap<OnRoutingChangedListener, NativeRoutingEventHandlerDelegate>
- mRoutingChangeListeners =
- new ArrayMap<OnRoutingChangedListener, NativeRoutingEventHandlerDelegate>();
+ @Override
+ default public void onRoutingChanged(AudioRouting router) {
+ if (router instanceof AudioRecord) {
+ onRoutingChanged((AudioRecord) router);
+ }
+ }
+ }
/**
* Adds an {@link OnRoutingChangedListener} to receive notifications of routing changes
@@ -1435,88 +1443,28 @@
* @param handler Specifies the {@link Handler} object for the thread on which to execute
* the callback. If <code>null</code>, the {@link Handler} associated with the main
* {@link Looper} will be used.
+ * @deprecated users should switch to the general purpose
+ * {@link AudioRouting.OnRoutingChangedListener} class instead.
*/
@Deprecated
public void addOnRoutingChangedListener(OnRoutingChangedListener listener,
android.os.Handler handler) {
- if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
- synchronized (mRoutingChangeListeners) {
- testEnableNativeRoutingCallbacks();
- mRoutingChangeListeners.put(
- listener, new NativeRoutingEventHandlerDelegate(this, listener,
- handler != null ? handler : new Handler(mInitializationLooper)));
- }
- }
+ addOnRoutingChangedListener((AudioRouting.OnRoutingChangedListener) listener, handler);
}
/**
* Removes an {@link OnRoutingChangedListener} which has been previously added
* to receive rerouting notifications.
* @param listener The previously added {@link OnRoutingChangedListener} interface to remove.
+ * @deprecated users should switch to the general purpose
+ * {@link AudioRouting.OnRoutingChangedListener} class instead.
*/
@Deprecated
public void removeOnRoutingChangedListener(OnRoutingChangedListener listener) {
- synchronized (mRoutingChangeListeners) {
- if (mRoutingChangeListeners.containsKey(listener)) {
- mRoutingChangeListeners.remove(listener);
- testDisableNativeRoutingCallbacks();
- }
- }
+ removeOnRoutingChangedListener((AudioRouting.OnRoutingChangedListener) listener);
}
/**
- * >= "N" Routing
- * Helper class to handle the forwarding of native events to the appropriate listener
- * (potentially) handled in a different thread
- */
- private class NativeNewRoutingEventHandlerDelegate {
- private final Handler mHandler;
-
- NativeNewRoutingEventHandlerDelegate(final AudioRecord record,
- final AudioRouting.OnRoutingChangedListener listener,
- Handler handler) {
- // find the looper for our new event handler
- Looper looper;
- if (handler != null) {
- looper = handler.getLooper();
- } else {
- // no given handler, use the looper the AudioRecord was created in
- looper = mInitializationLooper;
- }
-
- // construct the event handler with this looper
- if (looper != null) {
- // implement the event handler delegate
- mHandler = new Handler(looper) {
- @Override
- public void handleMessage(Message msg) {
- if (record == null) {
- return;
- }
- switch(msg.what) {
- case AudioSystem.NATIVE_EVENT_ROUTING_CHANGE:
- if (listener != null) {
- listener.onRoutingChanged(record);
- }
- break;
- default:
- loge("Unknown native event type: " + msg.what);
- break;
- }
- }
- };
- } else {
- mHandler = null;
- }
- }
-
- Handler getHandler() {
- return mHandler;
- }
- }
-
- /**
- * Marshmallow Routing
* Helper class to handle the forwarding of native events to the appropriate listener
* (potentially) handled in a different thread
*/
@@ -1524,7 +1472,7 @@
private final Handler mHandler;
NativeRoutingEventHandlerDelegate(final AudioRecord record,
- final OnRoutingChangedListener listener,
+ final AudioRouting.OnRoutingChangedListener listener,
Handler handler) {
// find the looper for our new event handler
Looper looper;
@@ -1571,26 +1519,12 @@
*/
private void broadcastRoutingChange() {
AudioManager.resetAudioPortGeneration();
- // Marshmallow Routing
- Collection<NativeRoutingEventHandlerDelegate> values;
synchronized (mRoutingChangeListeners) {
- values = mRoutingChangeListeners.values();
- }
- for(NativeRoutingEventHandlerDelegate delegate : values) {
- Handler handler = delegate.getHandler();
- if (handler != null) {
- handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
- }
- }
- // >= "N" Routing
- Collection<NativeNewRoutingEventHandlerDelegate> newValues;
- synchronized (mNewRoutingChangeListeners) {
- newValues = mNewRoutingChangeListeners.values();
- }
- for(NativeNewRoutingEventHandlerDelegate delegate : newValues) {
- Handler handler = delegate.getHandler();
- if (handler != null) {
- handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
+ for (NativeRoutingEventHandlerDelegate delegate : mRoutingChangeListeners.values()) {
+ Handler handler = delegate.getHandler();
+ if (handler != null) {
+ handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
+ }
}
}
}
@@ -1623,6 +1557,7 @@
* @return true if successful, false if the specified {@link AudioDeviceInfo} is non-null and
* does not correspond to a valid audio input device.
*/
+ @Override
public boolean setPreferredDevice(AudioDeviceInfo deviceInfo) {
// Do some validation....
if (deviceInfo != null && !deviceInfo.isSource()) {
@@ -1643,6 +1578,7 @@
* Returns the selected input specified by {@link #setPreferredDevice}. Note that this
* is not guarenteed to correspond to the actual device being used for recording.
*/
+ @Override
public AudioDeviceInfo getPreferredDevice() {
synchronized (this) {
return mPreferredDevice;
@@ -1683,7 +1619,6 @@
* (potentially) handled in a different thread
*/
private class NativeEventHandler extends Handler {
-
private final AudioRecord mAudioRecord;
NativeEventHandler(AudioRecord recorder, Looper looper) {
@@ -1714,8 +1649,7 @@
break;
}
}
- };
-
+ }
//---------------------------------------------------------
// Java methods called from the native side
@@ -1793,7 +1727,7 @@
private native final void native_enableDeviceCallback();
private native final void native_disableDeviceCallback();
- private native final int native_get_timestamp(@NonNull AudioTimestamp timestamp,
+ private native final int native_get_timestamp(@NonNull AudioTimestamp outTimestamp,
@AudioTimestamp.Timebase int timebase);
//---------------------------------------------------------
diff --git a/media/java/android/media/AudioRouting.java b/media/java/android/media/AudioRouting.java
index 41f92d4..26fa631 100644
--- a/media/java/android/media/AudioRouting.java
+++ b/media/java/android/media/AudioRouting.java
@@ -57,7 +57,7 @@
* the callback. If <code>null</code>, the {@link Handler} associated with the main
* {@link Looper} will be used.
*/
- public void addOnRoutingListener(OnRoutingChangedListener listener,
+ public void addOnRoutingChangedListener(OnRoutingChangedListener listener,
Handler handler);
/**
@@ -66,7 +66,7 @@
* @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
* to remove.
*/
- public void removeOnRoutingListener(OnRoutingChangedListener listener);
+ public void removeOnRoutingChangedListener(OnRoutingChangedListener listener);
/**
* Defines the interface by which applications can receive notifications of routing
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 621129d..9d360db 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -40,6 +40,7 @@
import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
/**
@@ -1489,6 +1490,7 @@
* @deprecated Applications should use {@link #setVolume} instead, as it
* more gracefully scales down to mono, and up to multi-channel content beyond stereo.
*/
+ @Deprecated
public int setStereoVolume(float leftGain, float rightGain) {
if (isRestricted()) {
return SUCCESS;
@@ -2397,6 +2399,7 @@
* @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
* does not correspond to a valid audio output device.
*/
+ @Override
public boolean setPreferredDevice(AudioDeviceInfo deviceInfo) {
// Do some validation....
if (deviceInfo != null && !deviceInfo.isSink()) {
@@ -2416,6 +2419,7 @@
* Returns the selected output specified by {@link #setPreferredDevice}. Note that this
* is not guaranteed to correspond to the actual device being used for playback.
*/
+ @Override
public AudioDeviceInfo getPreferredDevice() {
synchronized (this) {
return mPreferredDevice;
@@ -2427,6 +2431,7 @@
* Note: The query is only valid if the AudioTrack is currently playing. If it is not,
* <code>getRoutedDevice()</code> will return null.
*/
+ @Override
public AudioDeviceInfo getRoutedDevice() {
int deviceId = native_getRoutedDeviceId();
if (deviceId == 0) {
@@ -2445,8 +2450,8 @@
/*
* Call BEFORE adding a routing callback handler.
*/
- private void testEnableNativeRoutingCallbacks() {
- if (mRoutingChangeListeners.size() == 0 && mNewRoutingChangeListeners.size() == 0) {
+ private void testEnableNativeRoutingCallbacksLocked() {
+ if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback();
}
}
@@ -2454,24 +2459,23 @@
/*
* Call AFTER removing a routing callback handler.
*/
- private void testDisableNativeRoutingCallbacks() {
- if (mRoutingChangeListeners.size() == 0 && mNewRoutingChangeListeners.size() == 0) {
+ private void testDisableNativeRoutingCallbacksLocked() {
+ if (mRoutingChangeListeners.size() == 0) {
native_disableDeviceCallback();
}
}
//--------------------------------------------------------------------------
- // >= "N" (Re)Routing Info
+ // (Re)Routing Info
//--------------------
/**
* The list of AudioRouting.OnRoutingChangedListener interfaces added (with
- * {@link AudioTrack#addOnRoutingListener(AudioRouting.OnRoutingChangedListener,
- * android.os.Handler)}
- * by an app to receive (re)routing notifications.
+ * {@link AudioRecord#addOnRoutingChangedListener} by an app to receive
+ * (re)routing notifications.
*/
- private ArrayMap<AudioRouting.OnRoutingChangedListener, NativeNewRoutingEventHandlerDelegate>
- mNewRoutingChangeListeners =
- new ArrayMap<AudioRouting.OnRoutingChangedListener, NativeNewRoutingEventHandlerDelegate>();
+ @GuardedBy("mRoutingChangeListeners")
+ private ArrayMap<AudioRouting.OnRoutingChangedListener,
+ NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>();
/**
* Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
@@ -2482,14 +2486,15 @@
* the callback. If <code>null</code>, the {@link Handler} associated with the main
* {@link Looper} will be used.
*/
- public void addOnRoutingListener(AudioRouting.OnRoutingChangedListener listener,
+ @Override
+ public void addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener,
Handler handler) {
- if (listener != null && !mNewRoutingChangeListeners.containsKey(listener)) {
- synchronized (mNewRoutingChangeListeners) {
- testEnableNativeRoutingCallbacks();
- mNewRoutingChangeListeners.put(
- listener, new NativeNewRoutingEventHandlerDelegate(this, listener,
- handler != null ? handler : new Handler(mInitializationLooper)));
+ synchronized (mRoutingChangeListeners) {
+ if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
+ testEnableNativeRoutingCallbacksLocked();
+ mRoutingChangeListeners.put(
+ listener, new NativeRoutingEventHandlerDelegate(this, listener,
+ handler != null ? handler : new Handler(mInitializationLooper)));
}
}
}
@@ -2500,39 +2505,42 @@
* @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
* to remove.
*/
- public void removeOnRoutingListener(AudioRouting.OnRoutingChangedListener listener) {
- if (mNewRoutingChangeListeners.containsKey(listener)) {
- mNewRoutingChangeListeners.remove(listener);
+ @Override
+ public void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener) {
+ synchronized (mRoutingChangeListeners) {
+ if (mRoutingChangeListeners.containsKey(listener)) {
+ mRoutingChangeListeners.remove(listener);
+ }
+ testDisableNativeRoutingCallbacksLocked();
}
- testDisableNativeRoutingCallbacks();
}
//--------------------------------------------------------------------------
- // Marshmallow (Re)Routing Info
+ // (Re)Routing Info
//--------------------
/**
- * Defines the interface by which applications can receive notifications of routing
- * changes for the associated {@link AudioTrack}.
+ * Defines the interface by which applications can receive notifications of
+ * routing changes for the associated {@link AudioTrack}.
+ *
+ * @deprecated users should switch to the general purpose
+ * {@link AudioRouting.OnRoutingChangedListener} class instead.
*/
@Deprecated
- public interface OnRoutingChangedListener {
+ public interface OnRoutingChangedListener extends AudioRouting.OnRoutingChangedListener {
/**
- * Called when the routing of an AudioTrack changes from either and explicit or
- * policy rerouting. Use {@link #getRoutedDevice()} to retrieve the newly routed-to
- * device.
+ * Called when the routing of an AudioTrack changes from either and
+ * explicit or policy rerouting. Use {@link #getRoutedDevice()} to
+ * retrieve the newly routed-to device.
*/
- @Deprecated
public void onRoutingChanged(AudioTrack audioTrack);
- }
- /**
- * The list of AudioTrack.OnRoutingChangedListener interfaces added (with
- * {@link AudioTrack#addOnRoutingChangedListener(OnRoutingChangedListener, android.os.Handler)}
- * by an app to receive (re)routing notifications.
- */
- private ArrayMap<OnRoutingChangedListener, NativeRoutingEventHandlerDelegate>
- mRoutingChangeListeners =
- new ArrayMap<OnRoutingChangedListener, NativeRoutingEventHandlerDelegate>();
+ @Override
+ default public void onRoutingChanged(AudioRouting router) {
+ if (router instanceof AudioTrack) {
+ onRoutingChanged((AudioTrack) router);
+ }
+ }
+ }
/**
* Adds an {@link OnRoutingChangedListener} to receive notifications of routing changes
@@ -2542,33 +2550,25 @@
* @param handler Specifies the {@link Handler} object for the thread on which to execute
* the callback. If <code>null</code>, the {@link Handler} associated with the main
* {@link Looper} will be used.
+ * @deprecated users should switch to the general purpose
+ * {@link AudioRouting.OnRoutingChangedListener} class instead.
*/
@Deprecated
public void addOnRoutingChangedListener(OnRoutingChangedListener listener,
android.os.Handler handler) {
- if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
- synchronized (mRoutingChangeListeners) {
- testEnableNativeRoutingCallbacks();
- mRoutingChangeListeners.put(
- listener, new NativeRoutingEventHandlerDelegate(this, listener,
- handler != null ? handler : new Handler(mInitializationLooper)));
- }
- }
+ addOnRoutingChangedListener((AudioRouting.OnRoutingChangedListener) listener, handler);
}
/**
* Removes an {@link OnRoutingChangedListener} which has been previously added
* to receive rerouting notifications.
* @param listener The previously added {@link OnRoutingChangedListener} interface to remove.
+ * @deprecated users should switch to the general purpose
+ * {@link AudioRouting.OnRoutingChangedListener} class instead.
*/
@Deprecated
public void removeOnRoutingChangedListener(OnRoutingChangedListener listener) {
- synchronized (mRoutingChangeListeners) {
- if (mRoutingChangeListeners.containsKey(listener)) {
- mRoutingChangeListeners.remove(listener);
- }
- testDisableNativeRoutingCallbacks();
- }
+ removeOnRoutingChangedListener((AudioRouting.OnRoutingChangedListener) listener);
}
/**
@@ -2576,27 +2576,12 @@
*/
private void broadcastRoutingChange() {
AudioManager.resetAudioPortGeneration();
-
- // Marshmallow Routing
- Collection<NativeRoutingEventHandlerDelegate> values;
synchronized (mRoutingChangeListeners) {
- values = mRoutingChangeListeners.values();
- }
- for(NativeRoutingEventHandlerDelegate delegate : values) {
- Handler handler = delegate.getHandler();
- if (handler != null) {
- handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
- }
- }
- // >= "N" Routing
- Collection<NativeNewRoutingEventHandlerDelegate> newValues;
- synchronized (mNewRoutingChangeListeners) {
- newValues = mNewRoutingChangeListeners.values();
- }
- for(NativeNewRoutingEventHandlerDelegate delegate : newValues) {
- Handler handler = delegate.getHandler();
- if (handler != null) {
- handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
+ for (NativeRoutingEventHandlerDelegate delegate : mRoutingChangeListeners.values()) {
+ Handler handler = delegate.getHandler();
+ if (handler != null) {
+ handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
+ }
}
}
}
@@ -2681,7 +2666,6 @@
}
/**
- * Marshmallow Routing API.
* Helper class to handle the forwarding of native events to the appropriate listener
* (potentially) handled in a different thread
*/
@@ -2689,57 +2673,6 @@
private final Handler mHandler;
NativeRoutingEventHandlerDelegate(final AudioTrack track,
- final OnRoutingChangedListener listener,
- Handler handler) {
- // find the looper for our new event handler
- Looper looper;
- if (handler != null) {
- looper = handler.getLooper();
- } else {
- // no given handler, use the looper the AudioTrack was created in
- looper = mInitializationLooper;
- }
-
- // construct the event handler with this looper
- if (looper != null) {
- // implement the event handler delegate
- mHandler = new Handler(looper) {
- @Override
- public void handleMessage(Message msg) {
- if (track == null) {
- return;
- }
- switch(msg.what) {
- case AudioSystem.NATIVE_EVENT_ROUTING_CHANGE:
- if (listener != null) {
- listener.onRoutingChanged(track);
- }
- break;
- default:
- loge("Unknown native event type: " + msg.what);
- break;
- }
- }
- };
- } else {
- mHandler = null;
- }
- }
-
- Handler getHandler() {
- return mHandler;
- }
- }
-
- /**
- * Marshmallow Routing API.
- * Helper class to handle the forwarding of native events to the appropriate listener
- * (potentially) handled in a different thread
- */
- private class NativeNewRoutingEventHandlerDelegate {
- private final Handler mHandler;
-
- NativeNewRoutingEventHandlerDelegate(final AudioTrack track,
final AudioRouting.OnRoutingChangedListener listener,
Handler handler) {
// find the looper for our new event handler
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 2bd9781..7af9c24 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -2611,10 +2611,13 @@
public static final int VP8ProfileMain = 0x01;
// from OMX_VIDEO_VP9PROFILETYPE
- public static final int VP9Profile0 = 0x00;
- public static final int VP9Profile1 = 0x01;
- public static final int VP9Profile2 = 0x02;
- public static final int VP9Profile3 = 0x03;
+ public static final int VP9Profile0 = 0x01;
+ public static final int VP9Profile1 = 0x02;
+ public static final int VP9Profile2 = 0x04;
+ public static final int VP9Profile3 = 0x08;
+ // HDR profiles also support passing HDR metadata
+ public static final int VP9Profile2HDR = 0x1000;
+ public static final int VP9Profile3HDR = 0x2000;
// from OMX_VIDEO_VP9LEVELTYPE
public static final int VP9Level1 = 0x0;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index b78869e..55d5f42 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1107,6 +1107,9 @@
* as this call returns.
*
* @param afd the AssetFileDescriptor for the file you want to play
+ * @throws IllegalStateException if it is called in an invalid state
+ * @throws IllegalArgumentException if afd is not a valid AssetFileDescriptor
+ * @throws IOException if afd can not be read
*/
public void setDataSource(@NonNull AssetFileDescriptor afd)
throws IOException, IllegalArgumentException, IllegalStateException {
@@ -1127,6 +1130,8 @@
*
* @param fd the FileDescriptor for the file you want to play
* @throws IllegalStateException if it is called in an invalid state
+ * @throws IllegalArgumentException if fd is not a valid FileDescriptor
+ * @throws IOException if fd can not be read
*/
public void setDataSource(FileDescriptor fd)
throws IOException, IllegalArgumentException, IllegalStateException {
@@ -1143,6 +1148,8 @@
* @param offset the offset into the file where the data to be played starts, in bytes
* @param length the length in bytes of the data to be played
* @throws IllegalStateException if it is called in an invalid state
+ * @throws IllegalArgumentException if fd is not a valid FileDescriptor
+ * @throws IOException if fd can not be read
*/
public void setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException {
@@ -1157,6 +1164,7 @@
*
* @param dataSource the MediaDataSource for the media you want to play
* @throws IllegalStateException if it is called in an invalid state
+ * @throws IllegalArgumentException if dataSource is not a valid MediaDataSource
*/
public void setDataSource(MediaDataSource dataSource)
throws IllegalArgumentException, IllegalStateException {
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 03dc699..0b64876 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -369,8 +369,7 @@
}
/**
- * Returns the extras associated with this TV input.
- * @hide
+ * Returns domain-specific extras associated with this TV input.
*/
public Bundle getExtras() {
return mExtras;
@@ -847,11 +846,12 @@
}
/**
- * Sets the extras associated with this TV input.
+ * Sets domain-specific extras associated with this TV input.
*
- * @param extras The extras associated with this TV input.
+ * @param extras Domain-specific extras associated with this TV input. Keys <em>must</em> be
+ * a scoped name, i.e. prefixed with a package name you own, so that different
+ * developers will not create conflicting keys.
* @return This Builder object to allow for chaining of calls to builder methods.
- * @hide
*/
public Builder setExtras(Bundle extras) {
this.mExtras = extras;
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index e623353..d718a7e 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -20,9 +20,12 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import com.android.internal.util.Preconditions;
+import java.util.Objects;
+
/**
* Encapsulates the format of tracks played in {@link TvInputService}.
*/
@@ -245,6 +248,37 @@
dest.writeBundle(mExtra);
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof TvTrackInfo)) {
+ return false;
+ }
+
+ TvTrackInfo obj = (TvTrackInfo) o;
+ return TextUtils.equals(mId, obj.mId)
+ && mType == obj.mType
+ && TextUtils.equals(mLanguage, obj.mLanguage)
+ && TextUtils.equals(mDescription, obj.mDescription)
+ && Objects.equals(mExtra, obj.mExtra)
+ && (mType == TYPE_AUDIO
+ ? mAudioChannelCount == obj.mAudioChannelCount
+ && mAudioSampleRate == obj.mAudioSampleRate
+ : (mType == TYPE_VIDEO
+ ? mVideoWidth == obj.mVideoWidth
+ && mVideoHeight == obj.mVideoHeight
+ && mVideoFrameRate == obj.mVideoFrameRate
+ && mVideoPixelAspectRatio == obj.mVideoPixelAspectRatio : true));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mId);
+ }
+
public static final Parcelable.Creator<TvTrackInfo> CREATOR =
new Parcelable.Creator<TvTrackInfo>() {
@Override
diff --git a/packages/DocumentsUI/res/color/item_root_icon.xml b/packages/DocumentsUI/res/color/item_root_icon.xml
index 0aa2c13..e1d7e61 100644
--- a/packages/DocumentsUI/res/color/item_root_icon.xml
+++ b/packages/DocumentsUI/res/color/item_root_icon.xml
@@ -15,5 +15,10 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@*android:color/secondary_text_material_light" />
+ <item
+ android:state_activated="false"
+ android:color="@*android:color/secondary_text_material_light" />
+ <item
+ android:state_activated="true"
+ android:color="@color/root_activated_color" />
</selector>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 40b54d3..7a7d3a1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -232,12 +232,13 @@
final MenuItem list = menu.findItem(R.id.menu_list);
final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
- boolean recents = cwd == null;
- createDir.setVisible(picking && !recents && cwd.isCreateSupported());
+ createDir.setVisible(picking);
+ createDir.setEnabled(canCreateDirectory());
// No display options in recent directories
- if (picking && recents) {
+ boolean inRecents = cwd == null;
+ if (picking && inRecents) {
grid.setVisible(false);
list.setVisible(false);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
index f6fe47b..3373c23 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
@@ -147,17 +147,13 @@
}
}
- // Hamburger if drawer is present, else root icon, or sad nullness.
+ // Hamburger if drawer is present, else sad nullness.
private @Nullable Drawable getActionBarIcon() {
if (mDrawer.isPresent()) {
return mToolbar.getContext().getDrawable(R.drawable.ic_hamburger);
} else {
- RootInfo root = mEnv.getCurrentRoot();
- if (root != null) {
- return root.loadToolbarIcon(mToolbar.getContext());
- }
+ return null;
}
- return null;
}
void revealRootsDrawer(boolean open) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
index 8fcd9d1..babde99 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
@@ -17,7 +17,6 @@
package com.android.documentsui;
import static com.android.documentsui.Shared.DEBUG;
-import static com.android.documentsui.Shared.TAG;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
import android.content.ClipData;
@@ -45,6 +44,8 @@
* Provides support for gather a list of quick-viewable files into a quick view intent.
*/
final class QuickViewIntentBuilder {
+
+ private static final String TAG = "QuickViewIntentBuilder";
private static final int MAX_CLIP_ITEMS = 1000;
private final DocumentInfo mDocument;
@@ -127,8 +128,18 @@
for (int i = 0; i < siblingIds.length; i++) {
cursor = mModel.getItem(siblingIds[i]);
+ if (cursor == null) {
+ if (DEBUG) Log.d(TAG,
+ "Unable to obtain cursor for sibling document, modelId: "
+ + siblingIds[i]);
+ continue;
+ }
+
mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+ if (DEBUG) Log.d(TAG,
+ "Skipping directory, not supported by quick view. modelId: "
+ + siblingIds[i]);
continue;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index bc2133e..e7b2ed1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -351,7 +351,10 @@
private boolean handleViewItem(String id) {
final Cursor cursor = mModel.getItem(id);
- assert(cursor != null);
+ if (cursor == null) {
+ Log.w(TAG, "Can't view item. Can't obtain cursor for modeId" + id);
+ return false;
+ }
final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
@@ -465,11 +468,14 @@
public boolean onBeforeItemStateChange(String modelId, boolean selected) {
if (selected) {
final Cursor cursor = mModel.getItem(modelId);
-
- assert(cursor != null);
+ if (cursor == null) {
+ Log.w(TAG, "Can't obtain cursor for modelId: " + modelId);
+ return false;
+ }
final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+
return mTuner.canSelectType(docMimeType, docFlags);
}
return true;
@@ -479,7 +485,7 @@
public void onItemStateChanged(String modelId, boolean selected) {
final Cursor cursor = mModel.getItem(modelId);
if (cursor == null) {
- Log.e(TAG, "Model returned null cursor for document: " + modelId
+ Log.w(TAG, "Model returned null cursor for document: " + modelId
+ ". Ignoring state changed event.");
return;
}
@@ -833,6 +839,15 @@
getActivity(),
DocumentsActivity.class);
+
+ // Relay any config overrides bits present in the original intent.
+ Intent original = getActivity().getIntent();
+ if (original != null && original.hasExtra(Shared.EXTRA_PRODUCTIVITY_MODE)) {
+ intent.putExtra(
+ Shared.EXTRA_PRODUCTIVITY_MODE,
+ original.getBooleanExtra(Shared.EXTRA_PRODUCTIVITY_MODE, false));
+ }
+
// Set an appropriate title on the drawer when it is shown in the picker.
// Coupled with the fact that we auto-open the drawer for copy/move operations
// it should basically be the thing people see first.
@@ -1095,6 +1110,10 @@
List<String> enabled = new ArrayList<String>();
for (String id : mAdapter.getModelIds()) {
Cursor cursor = getModel().getItem(id);
+ if (cursor != null) {
+ Log.w(TAG, "Skipping selection. Can't obtain cursor for modeId: " + id);
+ continue;
+ }
String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
if (isDocumentEnabled(docMimeType, docFlags)) {
@@ -1183,7 +1202,10 @@
String id = getModelId(v);
if (id != null) {
Cursor dstCursor = mModel.getItem(id);
- assert(dstCursor != null);
+ if (dstCursor != null) {
+ Log.w(TAG, "Invalid destination. Can't obtain cursor for modelId: " + id);
+ return null;
+ }
return DocumentInfo.fromDirectoryCursor(dstCursor);
}
@@ -1256,8 +1278,10 @@
}
final Cursor cursor = mModel.getItem(modelId);
-
- assert(cursor != null);
+ if (cursor == null) {
+ Log.w(TAG, "Undraggable document. Can't obtain cursor for modelId " + modelId);
+ return Collections.EMPTY_LIST;
+ }
return Lists.newArrayList(
DocumentInfo.fromDirectoryCursor(cursor));
@@ -1386,7 +1410,7 @@
// Handle range selection adjustments. Extending the selection will adjust the
// bounds of the in-progress range selection. Each time an unshifted navigation
// event is received, the range selection is restarted.
- if (shouldExtendSelection(event)) {
+ if (shouldExtendSelection(doc, event)) {
if (!mSelectionManager.isRangeSelectionActive()) {
// Start a range selection if one isn't active
mSelectionManager.startRangeSelection(doc.getAdapterPosition());
@@ -1423,9 +1447,22 @@
return false;
}
- private boolean shouldExtendSelection(KeyEvent event) {
- return Events.isNavigationKeyCode(event.getKeyCode()) &&
- event.isShiftPressed();
+ private boolean shouldExtendSelection(DocumentHolder doc, KeyEvent event) {
+ if (!Events.isNavigationKeyCode(event.getKeyCode()) || !event.isShiftPressed()) {
+ return false;
+ }
+
+ // TODO: Combine this method with onBeforeItemStateChange, as both of them are almost
+ // the same, and responsible for the same thing (whether to select or not).
+ final Cursor cursor = mModel.getItem(doc.modelId);
+ if (cursor == null) {
+ Log.w(TAG, "Couldn't obtain cursor for modelId: " + doc.modelId);
+ return false;
+ }
+
+ final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+ return mTuner.canSelectType(docMimeType, docFlags);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
index 450341f..2288fe74 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
@@ -139,11 +139,13 @@
}
// Do everything in global coordinates - it makes things simpler.
- Rect rect = new Rect();
- mSelectionHotspot.getGlobalVisibleRect(rect);
+ int[] coords = new int[2];
+ mSelectionHotspot.getLocationOnScreen(coords);
+ Rect rect = new Rect(coords[0], coords[1], coords[0] + mSelectionHotspot.getWidth(),
+ coords[1] + mSelectionHotspot.getHeight());
// If the tap occurred within the icon rect, consider it a selection.
- if (rect.contains((int)event.getRawX(), (int)event.getRawY())) {
+ if (rect.contains((int) event.getRawX(), (int) event.getRawY())) {
return mEventListener.onSelect(this);
} else {
return mEventListener.onActivate(this);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
index ac05c05..f274df3 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.content.Context;
+import android.database.Cursor;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
@@ -476,9 +477,9 @@
List<String> index = new ArrayList<>(itemCount);
for (int i = 0; i < itemCount; i++) {
String modelId = mAdapter.getModelId(i);
- if (modelId != null) {
- String title =
- getCursorString(mModel.getItem(modelId), Document.COLUMN_DISPLAY_NAME);
+ Cursor cursor = mModel.getItem(modelId);
+ if (modelId != null && cursor != null) {
+ String title = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
// Perform case-insensitive search.
index.add(title.toLowerCase());
} else {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
index 3642b01..e2a28ad9a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
@@ -37,6 +37,7 @@
import com.android.documentsui.model.DocumentInfo;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -403,11 +404,18 @@
public @Nullable Cursor getItem(String modelId) {
Integer pos = mPositions.get(modelId);
- if (pos != null) {
- mCursor.moveToPosition(pos);
- return mCursor;
+ if (pos == null) {
+ if (DEBUG) Log.d(TAG, "Unabled to find cursor position for modelId: " + modelId);
+ return null;
}
- return null;
+
+ if (!mCursor.moveToPosition(pos)) {
+ if (DEBUG) Log.d(TAG,
+ "Unabled to move cursor to position " + pos + " for modelId: " + modelId);
+ return null;
+ }
+
+ return mCursor;
}
boolean isEmpty() {
@@ -424,8 +432,11 @@
final List<DocumentInfo> docs = new ArrayList<>(size);
for (String modelId: items.getAll()) {
final Cursor cursor = getItem(modelId);
- assert(cursor != null);
-
+ if (cursor == null) {
+ Log.w(TAG,
+ "Skipping document. Unabled to obtain cursor for modelId: " + modelId);
+ continue;
+ }
docs.add(DocumentInfo.fromDirectoryCursor(cursor));
}
return docs;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
index 1285b34..35d8988 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
@@ -378,8 +378,8 @@
* @param pos The anchor position for the selection range.
*/
void startRangeSelection(int pos) {
- attemptSelect(mAdapter.getModelId(pos));
- setSelectionRangeBegin(pos);
+ attemptSelect(mAdapter.getModelId(pos));
+ setSelectionRangeBegin(pos);
}
/**
@@ -1268,6 +1268,11 @@
notifySelectionChanged();
}
+ @Override
+ public boolean onBeforeItemStateChange(String id, boolean nextState) {
+ return notifyBeforeItemStateChange(id, nextState);
+ }
+
private class ViewScroller implements Runnable {
/**
* The number of milliseconds of scrolling at which scroll speed continues to increase.
@@ -1655,7 +1660,9 @@
if (id != null) {
// The adapter inserts items for UI layout purposes that aren't associated
// with files. Those will have a null model ID. Don't select them.
- mSelection.add(id);
+ if (canSelect(id)) {
+ mSelection.add(id);
+ }
}
if (isPossiblePositionNearestOrigin(column, columnStartIndex, columnEndIndex,
row, rowStartIndex, rowEndIndex)) {
@@ -1669,6 +1676,21 @@
}
/**
+ * @return True if the item is selectable.
+ */
+ private boolean canSelect(String id) {
+ // TODO: Simplify the logic, so the check whether we can select is done in one place.
+ // Consider injecting FragmentTuner, or move the checks from MultiSelectManager to
+ // Selection.
+ for (OnSelectionChangedListener listener : mOnSelectionChangedListeners) {
+ if (!listener.onBeforeItemStateChange(id, true)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* @return Returns true if the position is the nearest to the origin, or, in the case of the
* lower-right corner, whether it is possible that the position is the nearest to the
* origin. See comment below for reasoning for this special case.
@@ -1700,6 +1722,7 @@
*/
static interface OnSelectionChangedListener {
public void onSelectionChanged(Set<String> updatedSelection);
+ public boolean onBeforeItemStateChange(String id, boolean nextState);
}
void addOnSelectionChangedListener(OnSelectionChangedListener listener) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index 31ce837..3a86a51 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -139,11 +139,13 @@
};
public static DocumentInfo fromDirectoryCursor(Cursor cursor) {
+ assert(cursor != null);
final String authority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY);
return fromCursor(cursor, authority);
}
public static DocumentInfo fromCursor(Cursor cursor, String authority) {
+ assert(cursor != null);
final DocumentInfo info = new DocumentInfo();
info.updateFromCursor(cursor, authority);
return info;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index 0709652..d54bdfd 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -334,15 +334,6 @@
}
}
- public Drawable loadToolbarIcon(Context context) {
- if (derivedIcon != 0) {
- return IconUtils.applyTintAttr(context, derivedIcon,
- android.R.attr.colorControlNormal);
- } else {
- return IconUtils.loadPackageIcon(context, authority, icon);
- }
- }
-
@Override
public boolean equals(Object o) {
if (o == null) {
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java
index 0c0e0b7..cc119fe 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java
@@ -76,6 +76,11 @@
public void onSelectionChanged(Set<String> updatedSelection) {
lastSelection = updatedSelection;
}
+
+ @Override
+ public boolean onBeforeItemStateChange(String id, boolean nextState) {
+ return true;
+ }
});
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
index 329afdd..6ed4ea1 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
@@ -196,9 +196,10 @@
}
task.loadObjectInfoList(NUM_LOADING_ENTRIES);
final boolean shouldNotify =
- task.mLastNotified.getTime() <
- new Date().getTime() - NOTIFY_PERIOD_MS ||
- task.getState() != LoaderTask.STATE_LOADING;
+ task.getState() != LoaderTask.STATE_CANCELLED &&
+ (task.mLastNotified.getTime() <
+ new Date().getTime() - NOTIFY_PERIOD_MS ||
+ task.getState() != LoaderTask.STATE_LOADING);
if (shouldNotify) {
task.notify(mResolver);
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 00d31a71a..90dd440 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -16,6 +16,7 @@
package com.android.mtp;
+import android.annotation.Nullable;
import android.content.Context;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
@@ -27,6 +28,7 @@
import android.mtp.MtpDeviceInfo;
import android.mtp.MtpEvent;
import android.mtp.MtpObjectInfo;
+import android.mtp.MtpStorageInfo;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.util.Log;
@@ -80,9 +82,7 @@
}
}
- if (rawDevice == null) {
- throw new IOException("Not found USB device: " + deviceId);
- }
+ ensureNotNull(rawDevice, "Not found USB device: " + deviceId);
if (!mManager.hasPermission(rawDevice)) {
mManager.grantPermission(rawDevice);
@@ -93,10 +93,9 @@
final MtpDevice device = new MtpDevice(rawDevice);
- final UsbDeviceConnection connection = mManager.openDevice(rawDevice);
- if (connection == null) {
- throw new IOException("Failed to open a USB connection.");
- }
+ final UsbDeviceConnection connection = ensureNotNull(
+ mManager.openDevice(rawDevice),
+ "Failed to open a USB connection.");
if (!device.open(connection)) {
// We cannot open connection when another application use the device.
@@ -104,13 +103,11 @@
}
// Handle devices that fail to obtain storages just after opening a MTP session.
- final int[] storageIds = device.getStorageIds();
- if (storageIds == null) {
- throw new IOException("Not found MTP storages in the device.");
- }
+ final int[] storageIds = ensureNotNull(
+ device.getStorageIds(),
+ "Not found MTP storages in the device.");
mDevices.put(deviceId, device);
-
return createDeviceRecord(rawDevice);
}
@@ -133,11 +130,9 @@
MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException {
final MtpDevice device = getDevice(deviceId);
synchronized (device) {
- final MtpObjectInfo info = device.getObjectInfo(objectHandle);
- if (info == null) {
- throw new IOException("Failed to get object info: " + objectHandle);
- }
- return info;
+ return ensureNotNull(
+ device.getObjectInfo(objectHandle),
+ "Failed to get object info: " + objectHandle);
}
}
@@ -145,12 +140,9 @@
throws IOException {
final MtpDevice device = getDevice(deviceId);
synchronized (device) {
- final int[] handles =
- device.getObjectHandles(storageId, 0 /* all format */, parentObjectHandle);
- if (handles == null) {
- throw new IOException("Failed to fetch object handles.");
- }
- return handles;
+ return ensureNotNull(
+ device.getObjectHandles(storageId, 0 /* all format */, parentObjectHandle),
+ "Failed to fetch object handles.");
}
}
@@ -158,7 +150,9 @@
throws IOException {
final MtpDevice device = getDevice(deviceId);
synchronized (device) {
- return device.getObject(objectHandle, expectedSize);
+ return ensureNotNull(
+ device.getObject(objectHandle, expectedSize),
+ "Failed to fetch object bytes");
}
}
@@ -181,7 +175,9 @@
byte[] getThumbnail(int deviceId, int objectHandle) throws IOException {
final MtpDevice device = getDevice(deviceId);
synchronized (device) {
- return device.getThumbnail(objectHandle);
+ return ensureNotNull(
+ device.getThumbnail(objectHandle),
+ "Failed to obtain thumbnail bytes");
}
}
@@ -216,7 +212,7 @@
final MtpDevice device = getDevice(deviceId);
synchronized (device) {
final int result = (int) device.getParent(objectHandle);
- if (result < 0) {
+ if (result == 0xffffffff) {
throw new FileNotFoundException("Not found parent object");
}
return result;
@@ -227,7 +223,9 @@
throws IOException {
final MtpDevice device = getDevice(deviceId);
synchronized (device) {
- device.importFile(objectHandle, target);
+ if (!device.importFile(objectHandle, target)) {
+ throw new IOException("Failed to import file to FD");
+ }
}
}
@@ -243,26 +241,25 @@
}
private synchronized MtpDevice getDevice(int deviceId) throws IOException {
- final MtpDevice device = mDevices.get(deviceId);
- if (device == null) {
- throw new IOException("USB device " + deviceId + " is not opened.");
- }
- return device;
+ return ensureNotNull(
+ mDevices.get(deviceId),
+ "USB device " + deviceId + " is not opened.");
}
private MtpRoot[] getRoots(int deviceId) throws IOException {
final MtpDevice device = getDevice(deviceId);
synchronized (device) {
- final int[] storageIds = device.getStorageIds();
- if (storageIds == null) {
- throw new IOException("Failed to obtain storage IDs.");
- }
- final MtpRoot[] results = new MtpRoot[storageIds.length];
+ final int[] storageIds =
+ ensureNotNull(device.getStorageIds(), "Failed to obtain storage IDs.");
+ final ArrayList<MtpRoot> roots = new ArrayList<>();
for (int i = 0; i < storageIds.length; i++) {
- results[i] = new MtpRoot(
- device.getDeviceId(), device.getStorageInfo(storageIds[i]));
+ final MtpStorageInfo info = device.getStorageInfo(storageIds[i]);
+ if (info == null) {
+ continue;
+ }
+ roots.add(new MtpRoot(device.getDeviceId(), info));
}
- return results;
+ return roots.toArray(new MtpRoot[roots.size()]);
}
}
@@ -313,4 +310,12 @@
}
return false;
}
+
+ private static <T> T ensureNotNull(@Nullable T t, String errorMessage) throws IOException {
+ if (t != null) {
+ return t;
+ } else {
+ throw new IOException(errorMessage);
+ }
+ }
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
index 60dd7e1..a3c6bd7 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
@@ -143,9 +143,9 @@
}
}
- public void testCancelTask() throws IOException, InterruptedException {
+ public void testCancelTask() throws IOException, InterruptedException, TimeoutException {
setUpDocument(mManager,
- DocumentLoader.NUM_INITIAL_ENTRIES + DocumentLoader.NUM_LOADING_ENTRIES + 1);
+ DocumentLoader.NUM_INITIAL_ENTRIES + 1);
// Block the first iteration in the background thread.
mManager.blockDocument(
@@ -155,19 +155,24 @@
MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier)) {
assertTrue(cursor.getExtras().getBoolean(DocumentsContract.EXTRA_LOADING));
}
- Thread.sleep(DocumentLoader.NOTIFY_PERIOD_MS);
+
+ final Uri uri = DocumentsContract.buildChildDocumentsUri(
+ MtpDocumentsProvider.AUTHORITY, mParentIdentifier.mDocumentId);
+ assertEquals(0, mResolver.getChangeCount(uri));
// Clear task while the first iteration is being blocked.
+ mLoader.cancelTask(mParentIdentifier);
mManager.unblockDocument(
0, DocumentLoader.NUM_INITIAL_ENTRIES + 1);
- mLoader.cancelTask(mParentIdentifier);
-
- Thread.sleep(DocumentLoader.NOTIFY_PERIOD_MS * 2);
+ Thread.sleep(DocumentLoader.NOTIFY_PERIOD_MS);
+ assertEquals(0, mResolver.getChangeCount(uri));
// Check if it's OK to query invalidated task.
try (final Cursor cursor = mLoader.queryChildDocuments(
MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier)) {
+ assertTrue(cursor.getExtras().getBoolean(DocumentsContract.EXTRA_LOADING));
}
+ mResolver.waitForNotification(uri, 1);
}
private void setUpLoader() {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 084acac..985fe3c 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -804,7 +804,7 @@
<string name="disabled_by_admin">Disabled by administrator</string>
<!-- Option in navigation drawer that leads to Settings main screen [CHAR LIMIT=30] -->
- <string name="home">Home</string>
+ <string name="home">Settings Home</string>
<string-array name="battery_labels" translatable="false">
<item>0%</item>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index d353f31..8881034 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -534,6 +534,7 @@
Comparator<AppEntry> mRebuildComparator;
ArrayList<AppEntry> mRebuildResult;
ArrayList<AppEntry> mLastAppList;
+ boolean mRebuildForeground;
Session(Callbacks callbacks) {
mCallbacks = callbacks;
@@ -572,6 +573,11 @@
// Creates a new list of app entries with the given filter and comparator.
public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
+ return rebuild(filter, comparator, true);
+ }
+
+ public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator,
+ boolean foreground) {
synchronized (mRebuildSync) {
synchronized (mEntriesMap) {
mRebuildingSessions.add(this);
@@ -579,6 +585,7 @@
mRebuildAsync = false;
mRebuildFilter = filter;
mRebuildComparator = comparator;
+ mRebuildForeground = foreground;
mRebuildResult = null;
if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
Message msg = mBackgroundHandler.obtainMessage(
@@ -620,10 +627,12 @@
mRebuildRequested = false;
mRebuildFilter = null;
mRebuildComparator = null;
+ if (mRebuildForeground) {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
+ mRebuildForeground = false;
+ }
}
- Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
-
if (filter != null) {
filter.init();
}
@@ -640,7 +649,10 @@
if (filter == null || filter.filterApp(entry)) {
synchronized (mEntriesMap) {
if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
- entry.ensureLabel(mContext);
+ if (comparator != null) {
+ // Only need the label if we are going to be sorting.
+ entry.ensureLabel(mContext);
+ }
if (DEBUG) Log.i(TAG, "Using " + entry.info.packageName + ": " + entry);
filteredApps.add(entry);
if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
@@ -648,7 +660,9 @@
}
}
- Collections.sort(filteredApps, comparator);
+ if (comparator != null) {
+ Collections.sort(filteredApps, comparator);
+ }
synchronized (mRebuildSync) {
if (!mRebuildRequested) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index ff70190..bcbc6ac 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -216,6 +216,8 @@
if (sDashboardCategories == null) {
sTileCache = new HashMap<>();
sConfigTracker = new InterestingConfigChanges();
+ // Apply initial current config.
+ sConfigTracker.applyNewConfig(getResources());
sDashboardCategories = TileUtils.getCategories(this, sTileCache);
}
return sDashboardCategories;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 5621642..8dc247a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1324,6 +1324,9 @@
}
private static void appendSettingToCursor(MatrixCursor cursor, Setting setting) {
+ if (setting == null) {
+ return;
+ }
final int columnCount = cursor.getColumnCount();
String[] values = new String[columnCount];
@@ -1831,14 +1834,11 @@
private void maybeNotifyProfiles(int userId, Uri uri, String name,
Set<String> keysCloned) {
if (keysCloned.contains(name)) {
- List<UserInfo> profiles = mUserManager.getProfiles(userId);
- int size = profiles.size();
- for (int i = 0; i < size; i++) {
- UserInfo profile = profiles.get(i);
+ for (int profileId : mUserManager.getProfileIdsWithDisabled(userId)) {
// the notification for userId has already been sent.
- if (profile.id != userId) {
+ if (profileId != userId) {
mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED,
- profile.id, 0, uri).sendToTarget();
+ profileId, 0, uri).sendToTarget();
}
}
}
diff --git a/packages/Shell/res/values/strings.xml b/packages/Shell/res/values/strings.xml
index 5d90189..bee15dd 100644
--- a/packages/Shell/res/values/strings.xml
+++ b/packages/Shell/res/values/strings.xml
@@ -20,8 +20,6 @@
<string name="bugreport_in_progress_title">Bug report <xliff:g id="id">#%d</xliff:g> is being generated</string>
<!-- Title of notification indicating a bugreport has been successfully captured. [CHAR LIMIT=50] -->
<string name="bugreport_finished_title">Bug report <xliff:g id="id">#%d</xliff:g> captured</string>
- <!-- Title of notification indicating a bugreport has been successfully captured, but screenshot is not finished yet. [CHAR LIMIT=50] -->
- <string name="bugreport_finished_pending_screenshot_title">Bug report <xliff:g id="id">#%d</xliff:g> captured but screenshot pending</string>
<!-- Title of notification indicating a bugreport is being updated before it can be shared. [CHAR LIMIT=50] -->
<string name="bugreport_updating_title">Adding details to the bug report</string>
<!-- Content notification indicating a bugreport is being updated before it can be shared, asking the user to wait [CHAR LIMIT=50] -->
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 7ca7614..f0ddcb9 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -220,6 +220,7 @@
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.v(TAG, "onStartCommand(): " + dumpIntent(intent));
if (intent != null) {
// Handle it in a separate thread.
final Message msg = mMainHandler.obtainMessage();
@@ -297,6 +298,7 @@
return;
}
final Parcelable parcel = ((Intent) msg.obj).getParcelableExtra(EXTRA_ORIGINAL_INTENT);
+ Log.v(TAG, "handleMessage(): " + dumpIntent((Intent) parcel));
final Intent intent;
if (parcel instanceof Intent) {
// The real intent was passed to BugreportReceiver, which delegated to the service.
@@ -707,7 +709,8 @@
for (int i = 0; i < mProcesses.size(); i++) {
final BugreportInfo info = mProcesses.valueAt(i);
if (info.finished) {
- Log.d(TAG, "Not updating progress because share notification was already sent");
+ Log.d(TAG, "Not updating progress for " + info.id + " while taking screenshot"
+ + " because share notification was already sent");
continue;
}
updateProgress(info);
@@ -846,7 +849,15 @@
private static Intent buildSendIntent(Context context, BugreportInfo info) {
// Files are kept on private storage, so turn into Uris that we can
// grant temporary permissions for.
- final Uri bugreportUri = getUri(context, info.bugreportFile);
+ final Uri bugreportUri;
+ try {
+ bugreportUri = getUri(context, info.bugreportFile);
+ } catch (IllegalArgumentException e) {
+ // Should not happen on production, but happens when a Shell is sideloaded and
+ // FileProvider cannot find a configured root for it.
+ Log.wtf(TAG, "Could not get URI for " + info.bugreportFile, e);
+ return null;
+ }
final Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
final String mimeType = "application/vnd.android.bugreport";
@@ -907,6 +918,12 @@
addDetailsToZipFile(mContext, info);
final Intent sendIntent = buildSendIntent(mContext, info);
+ if (sendIntent == null) {
+ Log.w(TAG, "Stopping progres on ID " + id + " because share intent could not be built");
+ stopProgress(id);
+ return;
+ }
+
final Intent notifIntent;
// Send through warning dialog by default
@@ -939,15 +956,10 @@
shareIntent.putExtra(EXTRA_ID, info.id);
shareIntent.putExtra(EXTRA_INFO, info);
- final String title, content;
- if (takingScreenshot) {
- title = context.getString(R.string.bugreport_finished_pending_screenshot_title,
- info.id);
- content = context.getString(R.string.bugreport_finished_pending_screenshot_text);
- } else {
- title = context.getString(R.string.bugreport_finished_title, info.id);
- content = context.getString(R.string.bugreport_finished_text);
- }
+ final String title = context.getString(R.string.bugreport_finished_title, info.id);
+ final String content = takingScreenshot ?
+ context.getString(R.string.bugreport_finished_pending_screenshot_text)
+ : context.getString(R.string.bugreport_finished_text);
final Notification.Builder builder = newBaseNotification(context)
.setContentTitle(title)
.setTicker(title)
@@ -1165,6 +1177,52 @@
}
}
+ /**
+ * Dumps an intent, extracting the relevant extras.
+ */
+ static String dumpIntent(Intent intent) {
+ if (intent == null) {
+ return "NO INTENT";
+ }
+ String action = intent.getAction();
+ if (action == null) {
+ // Happens when BugreportReceiver calls startService...
+ action = "no action";
+ }
+ final StringBuilder buffer = new StringBuilder(action).append(" extras: ");
+ addExtra(buffer, intent, EXTRA_ID);
+ addExtra(buffer, intent, EXTRA_PID);
+ addExtra(buffer, intent, EXTRA_MAX);
+ addExtra(buffer, intent, EXTRA_NAME);
+ addExtra(buffer, intent, EXTRA_DESCRIPTION);
+ addExtra(buffer, intent, EXTRA_BUGREPORT);
+ addExtra(buffer, intent, EXTRA_SCREENSHOT);
+ addExtra(buffer, intent, EXTRA_INFO);
+
+ if (intent.hasExtra(EXTRA_ORIGINAL_INTENT)) {
+ buffer.append(SHORT_EXTRA_ORIGINAL_INTENT).append(": ");
+ final Intent originalIntent = intent.getParcelableExtra(EXTRA_ORIGINAL_INTENT);
+ buffer.append(dumpIntent(originalIntent));
+ } else {
+ buffer.append("no ").append(SHORT_EXTRA_ORIGINAL_INTENT);
+ }
+
+ return buffer.toString();
+ }
+
+ private static final String SHORT_EXTRA_ORIGINAL_INTENT =
+ EXTRA_ORIGINAL_INTENT.substring(EXTRA_ORIGINAL_INTENT.lastIndexOf('.') + 1);
+
+ private static void addExtra(StringBuilder buffer, Intent intent, String name) {
+ final String shortName = name.substring(name.lastIndexOf('.') + 1);
+ if (intent.hasExtra(name)) {
+ buffer.append(shortName).append('=').append(intent.getExtra(name));
+ } else {
+ buffer.append("no ").append(shortName);
+ }
+ buffer.append(", ");
+ }
+
private static boolean setSystemProperty(String key, String value) {
try {
if (DEBUG) Log.v(TAG, "Setting system property " + key + " to " + value);
diff --git a/packages/Shell/src/com/android/shell/BugreportReceiver.java b/packages/Shell/src/com/android/shell/BugreportReceiver.java
index cbd17bf..f6e558f 100644
--- a/packages/Shell/src/com/android/shell/BugreportReceiver.java
+++ b/packages/Shell/src/com/android/shell/BugreportReceiver.java
@@ -20,6 +20,7 @@
import static com.android.shell.BugreportProgressService.EXTRA_ORIGINAL_INTENT;
import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_FINISHED;
import static com.android.shell.BugreportProgressService.getFileExtra;
+import static com.android.shell.BugreportProgressService.dumpIntent;
import java.io.File;
@@ -51,7 +52,7 @@
@Override
public void onReceive(Context context, Intent intent) {
- Log.d(TAG, "onReceive: " + intent);
+ Log.d(TAG, "onReceive(): " + dumpIntent(intent));
// Clean up older bugreports in background
cleanupOldFiles(this, intent, INTENT_BUGREPORT_FINISHED, MIN_KEEP_COUNT, MIN_KEEP_AGE);
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index e1e0c3b..3eb7754 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -131,9 +131,6 @@
private static final boolean RENAMED_SCREENSHOTS = true;
private static final boolean DIDNT_RENAME_SCREENSHOTS = false;
- private static final boolean PENDING_SCREENSHOT = true;
- private static final boolean NOT_PENDING_SCREENSHOT = false;
-
private String mDescription;
private String mPlainTextPath;
@@ -179,42 +176,37 @@
sendBugreportStarted(1000);
waitForScreenshotButtonEnabled(true);
- final NumberFormat nf = NumberFormat.getPercentInstance();
- nf.setMinimumFractionDigits(2);
- nf.setMaximumFractionDigits(2);
-
- assertProgressNotification(NAME, nf.format(0));
+ assertProgressNotification(NAME, 0f);
SystemProperties.set(PROGRESS_PROPERTY, "108");
- assertProgressNotification(NAME, nf.format(0.108));
+ assertProgressNotification(NAME, 10.80f);
- SystemProperties.set(PROGRESS_PROPERTY, "500");
- assertProgressNotification(NAME, nf.format(0.50));
+ assertProgressNotification(NAME, 50.00f);
SystemProperties.set(PROGRESS_PROPERTY, "950");
- assertProgressNotification(NAME, nf.format(0.95));
+ assertProgressNotification(NAME, 95.00f);
// Make sure progress never goes back...
SystemProperties.set(MAX_PROPERTY, "2000");
Thread.sleep(POLLING_FREQUENCY + DateUtils.SECOND_IN_MILLIS);
- assertProgressNotification(NAME, nf.format(0.95));
+ assertProgressNotification(NAME, 95.00f);
SystemProperties.set(PROGRESS_PROPERTY, "1000");
- assertProgressNotification(NAME, nf.format(0.95));
+ assertProgressNotification(NAME, 95.00f);
// ...only forward...
SystemProperties.set(PROGRESS_PROPERTY, "1902");
- assertProgressNotification(NAME, nf.format(0.9510));
+ assertProgressNotification(NAME, 95.10f);
SystemProperties.set(PROGRESS_PROPERTY, "1960");
- assertProgressNotification(NAME, nf.format(0.98));
+ assertProgressNotification(NAME, 98.00f);
// ...but never more than the capped value.
SystemProperties.set(PROGRESS_PROPERTY, "2000");
- assertProgressNotification(NAME, nf.format(0.99));
+ assertProgressNotification(NAME, 99.00f);
SystemProperties.set(PROGRESS_PROPERTY, "3000");
- assertProgressNotification(NAME, nf.format(0.99));
+ assertProgressNotification(NAME, 99.00f);
Bundle extras =
sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath, mScreenshotPath);
@@ -233,7 +225,7 @@
nf.setMinimumFractionDigits(2);
nf.setMaximumFractionDigits(2);
- assertProgressNotification(NAME, nf.format(0));
+ assertProgressNotification(NAME, 00.00f);
openProgressNotification(ID);
UiObject cancelButton = mUiBot.getVisibleObject(mContext.getString(
@@ -338,7 +330,7 @@
detailsUi.clickOk();
assertPropertyValue(NAME_PROPERTY, NEW_NAME);
- assertProgressNotification(NEW_NAME, "0.00%");
+ assertProgressNotification(NEW_NAME, 00.00f);
Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath,
mScreenshotPath);
@@ -375,7 +367,7 @@
detailsUi.clickOk();
assertPropertyValue(NAME_PROPERTY, NEW_NAME);
- assertProgressNotification(NEW_NAME, "0.00%");
+ assertProgressNotification(NEW_NAME, 00.00f);
Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID,
plainText? mPlainTextPath : mZipPath, mScreenshotPath);
@@ -437,7 +429,7 @@
sendBugreportStarted(ID2, PID2, NAME2, 1000);
sendBugreportFinished(ID, mZipPath, mScreenshotPath);
- Bundle extras = acceptBugreportAndGetSharedIntent(ID, PENDING_SCREENSHOT);
+ Bundle extras = acceptBugreportAndGetSharedIntent(ID);
detailsUi = new DetailsUi(mUiBot, ID2);
detailsUi.assertName(NAME2);
@@ -571,13 +563,13 @@
}
}
- private void assertProgressNotification(String name, String percent) {
+ private void assertProgressNotification(String name, float percent) {
// TODO: it currently looks for 3 distinct objects, without taking advantage of their
// relationship.
openProgressNotification(ID);
Log.v(TAG, "Looking for progress notification details: '" + name + "-" + percent + "'");
mUiBot.getObject(name);
- mUiBot.getObject(percent);
+ // TODO: need a way to get the ProgresBar from the "android:id/progress" UIObject...
}
private UiObject openProgressNotification(int id) {
@@ -628,7 +620,7 @@
private Bundle sendBugreportFinishedAndGetSharedIntent(int id, String bugreportPath,
String screenshotPath) {
sendBugreportFinished(id, bugreportPath, screenshotPath);
- return acceptBugreportAndGetSharedIntent(id, NOT_PENDING_SCREENSHOT);
+ return acceptBugreportAndGetSharedIntent(id);
}
/**
@@ -637,11 +629,7 @@
* @return extras sent in the shared intent.
*/
private Bundle acceptBugreportAndGetSharedIntent(int id) {
- return acceptBugreportAndGetSharedIntent(id, NOT_PENDING_SCREENSHOT);
- }
-
- private Bundle acceptBugreportAndGetSharedIntent(int id, boolean pendingScreenshot) {
- acceptBugreport(id, pendingScreenshot);
+ acceptBugreport(id);
mUiBot.chooseActivity(UI_NAME);
return mListener.getExtras();
}
@@ -657,13 +645,7 @@
* Accepts the notification to share the finished bugreport.
*/
private void acceptBugreport(int id) {
- acceptBugreport(id, NOT_PENDING_SCREENSHOT);
- }
-
- private void acceptBugreport(int id, boolean pendingScreenshot) {
- final int res = pendingScreenshot ? R.string.bugreport_finished_pending_screenshot_title
- : R.string.bugreport_finished_title;
- mUiBot.clickOnNotification(mContext.getString(res, id));
+ mUiBot.clickOnNotification(mContext.getString(R.string.bugreport_finished_title, id));
}
/**
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 9e2442c..c248adf 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -159,6 +159,9 @@
<!-- DND access -->
<uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS" />
+ <!-- It's like, reality, but, you know, virtual -->
+ <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" />
+
<application
android:name=".SystemUIApplication"
android:persistent="true"
@@ -235,6 +238,19 @@
android:value="com.android.settings.category.system" />
</activity>
+ <activity-alias android:name=".DemoMode"
+ android:targetActivity=".tuner.TunerActivity"
+ android:icon="@drawable/tuner"
+ android:theme="@style/TunerSettings"
+ android:label="@string/demo_mode"
+ android:process=":tuner"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.settings.action.DEMO_MODE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity-alias>
+
<!-- Service used by secondary users to register themselves with the system user. -->
<service android:name=".recents.RecentsSystemUserService"
android:exported="false"
@@ -488,5 +504,12 @@
<action android:name="com.android.systemui.action.CLEAR_TUNER" />
</intent-filter>
</receiver>
+
+ <receiver
+ android:name=".statusbar.KeyboardShortcutsReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.SHOW_KEYBOARD_SHORTCUTS" />
+ </intent-filter>
+ </receiver>
</application>
</manifest>
diff --git a/packages/SystemUI/res/anim/tv_pip_overlay_fade_in_animation.xml b/packages/SystemUI/res/anim/tv_pip_overlay_fade_in_animation.xml
new file mode 100644
index 0000000..33bceaa
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_overlay_fade_in_animation.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:propertyName="alpha"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="350" />
diff --git a/packages/SystemUI/res/anim/tv_pip_overlay_fade_out_animation.xml b/packages/SystemUI/res/anim/tv_pip_overlay_fade_out_animation.xml
new file mode 100644
index 0000000..a12ddff
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_overlay_fade_out_animation.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:propertyName="alpha"
+ android:valueTo="0"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="500" />
diff --git a/packages/SystemUI/res/drawable/ksh_key_item_background.xml b/packages/SystemUI/res/drawable/ksh_key_item_background.xml
new file mode 100644
index 0000000..75ff30d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ksh_key_item_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 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
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/ksh_key_item_background" />
+ <corners android:radius="2dp" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/recents_info_dark.xml b/packages/SystemUI/res/drawable/recents_info_dark.xml
index b1a2242..555a69a 100644
--- a/packages/SystemUI/res/drawable/recents_info_dark.xml
+++ b/packages/SystemUI/res/drawable/recents_info_dark.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48.0dp"
- android:height="48.0dp"
+ android:width="24.0dp"
+ android:height="24.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
diff --git a/packages/SystemUI/res/drawable/recents_info_light.xml b/packages/SystemUI/res/drawable/recents_info_light.xml
index bc58c3b..65e7bf5 100644
--- a/packages/SystemUI/res/drawable/recents_info_light.xml
+++ b/packages/SystemUI/res/drawable/recents_info_light.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48.0dp"
- android:height="48.0dp"
+ android:width="24.0dp"
+ android:height="24.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
diff --git a/packages/SystemUI/res/layout/docked_stack_divider.xml b/packages/SystemUI/res/layout/docked_stack_divider.xml
index cfaf018..70e5451 100644
--- a/packages/SystemUI/res/layout/docked_stack_divider.xml
+++ b/packages/SystemUI/res/layout/docked_stack_divider.xml
@@ -24,6 +24,11 @@
android:id="@+id/docked_divider_background"
android:background="@color/docked_divider_background"/>
+ <com.android.systemui.stackdivider.MinimizedDockShadow
+ style="@style/DockedDividerMinimizedShadow"
+ android:id="@+id/minimized_dock_shadow"
+ android:alpha="0"/>">
+
<com.android.systemui.stackdivider.DividerHandleView
style="@style/DockedDividerHandle"
android:id="@+id/docked_divider_handle"
diff --git a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
index 3865020..63b759b 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
@@ -15,10 +15,10 @@
~ limitations under the License
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyboard_shortcuts_keyword_wrapper"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:minHeight="48dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:paddingBottom="8dp">
@@ -29,7 +29,8 @@
android:layout_marginEnd="32dp"
android:layout_gravity="center_vertical"
android:visibility="gone"
- android:layout_alignParentStart="true"/>
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"/>
<TextView
android:id="@+id/keyboard_shortcuts_keyword"
android:layout_toEndOf="@+id/keyboard_shortcuts_icon"
@@ -41,7 +42,8 @@
android:maxLines="5"
android:singleLine="false"
android:scrollHorizontally="false"
- android:layout_alignParentStart="true"/>
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"/>
<com.android.systemui.statusbar.KeyboardShortcutKeysLayout
android:id="@+id/keyboard_shortcuts_item_container"
android:layout_toEndOf="@+id/keyboard_shortcuts_keyword"
@@ -50,5 +52,6 @@
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:textSize="14sp"
- android:scrollHorizontally="false"/>
+ android:scrollHorizontally="false"
+ android:layout_centerVertical="true"/>
</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
index 0cecb96..5db6789 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
@@ -21,4 +21,4 @@
android:padding="@dimen/ksh_item_padding"
android:layout_marginStart="@dimen/ksh_item_margin_start"
android:scaleType="fitXY"
- android:background="@color/ksh_key_item_background"/>
+ android:background="@drawable/ksh_key_item_background"/>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
index 1215029..31a8773 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
@@ -19,7 +19,8 @@
android:layout_height="wrap_content"
android:padding="@dimen/ksh_item_padding"
android:layout_marginStart="@dimen/ksh_item_margin_start"
- android:background="@color/ksh_key_item_background"
+ android:background="@drawable/ksh_key_item_background"
android:textColor="@color/ksh_key_item_color"
android:singleLine="true"
+ android:gravity="center"
android:textSize="@dimen/ksh_item_text_size"/>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index b88846b..5d3b5ff 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -84,6 +84,7 @@
android:clickable="true"
android:focusable="true"
android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/accessibility_quick_settings_expand"
android:padding="12dp" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/recents.xml b/packages/SystemUI/res/layout/recents.xml
index 186aaf6..ae89631 100644
--- a/packages/SystemUI/res/layout/recents.xml
+++ b/packages/SystemUI/res/layout/recents.xml
@@ -25,6 +25,14 @@
android:layout_height="match_parent">
</com.android.systemui.recents.views.RecentsView>
+ <!-- Incompatible task overlay -->
+ <ViewStub android:id="@+id/incompatible_app_overlay_stub"
+ android:inflatedId="@+id/incompatible_app_overlay"
+ android:layout="@layout/recents_incompatible_app_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="128dp"
+ android:layout_gravity="center_horizontal|top" />
+
<!-- Nav Bar Scrim View -->
<ImageView
android:id="@+id/nav_bar_scrim"
diff --git a/packages/SystemUI/res/layout/recents_incompatible_app_overlay.xml b/packages/SystemUI/res/layout/recents_incompatible_app_overlay.xml
new file mode 100644
index 0000000..2b49dd3
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_incompatible_app_overlay.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:alpha="0"
+ android:background="#88000000"
+ android:forceHasOverlappingRendering="false">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:drawableTop="@drawable/recents_info_light"
+ android:drawablePadding="8dp"
+ android:text="@string/recents_incompatible_app_message" />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index c813818..b1b2f1e 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -26,7 +26,9 @@
android:id="@+id/task_view_thumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent" />
+
<include layout="@layout/recents_task_view_header" />
+
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/lock_to_app_fab"
android:layout_width="@dimen/recents_lock_to_app_size"
@@ -45,6 +47,17 @@
android:layout_gravity="center"
android:src="@drawable/recents_lock_to_app_pin" />
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+
+ <!-- The incompatible app toast -->
+ <ViewStub android:id="@+id/incompatible_app_toast_stub"
+ android:inflatedId="@+id/incompatible_app_toast"
+ android:layout="@*android:layout/transient_notification"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|center_horizontal"
+ android:layout_marginTop="48dp"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp" />
</FrameLayout>
</com.android.systemui.recents.views.TaskView>
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index 2b3c5df..2df57bf 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -31,40 +31,19 @@
android:paddingBottom="8dp"
android:paddingStart="16dp"
android:paddingEnd="12dp" />
- <LinearLayout
- android:id="@+id/title_container"
+ <TextView
+ android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
- android:orientation="vertical">
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="start"
- android:textSize="16sp"
- android:textColor="#ffffffff"
- android:text="@string/recents_empty_message"
- android:fontFamily="sans-serif-medium"
- android:singleLine="true"
- android:maxLines="1"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal" />
- <TextView
- android:id="@+id/sub_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="start"
- android:textSize="11sp"
- android:textColor="#ffffffff"
- android:text="@string/recents_launch_non_dockable_task_label"
- android:fontFamily="sans-serif-medium"
- android:singleLine="true"
- android:maxLines="1"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:visibility="gone" />
- </LinearLayout>
+ android:textSize="16sp"
+ android:textColor="#ffffffff"
+ android:text="@string/recents_empty_message"
+ android:fontFamily="sans-serif-medium"
+ android:singleLine="true"
+ android:maxLines="1"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
<com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/move_task"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values-land/styles.xml b/packages/SystemUI/res/values-land/styles.xml
index b711faa..82cba58 100644
--- a/packages/SystemUI/res/values-land/styles.xml
+++ b/packages/SystemUI/res/values-land/styles.xml
@@ -31,4 +31,8 @@
<item name="android:layout_height">96dp</item>
</style>
+ <style name="DockedDividerMinimizedShadow">
+ <item name="android:layout_width">8dp</item>
+ <item name="android:layout_height">match_parent</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index d9fcf42..d26fb06 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -151,7 +151,9 @@
<color name="docked_divider_background">#ff000000</color>
<color name="docked_divider_handle">#ffffff</color>
- <drawable name="forced_resizable_background">#40000000</drawable>
+ <drawable name="forced_resizable_background">#59000000</drawable>
+ <color name="minimize_dock_shadow_start">#60000000</color>
+ <color name="minimize_dock_shadow_end">#00000000</color>
<color name="default_remote_input_background">@*android:color/notification_default_color</color>
<color name="remote_input_hint">#99ffffff</color>
@@ -166,11 +168,11 @@
<color name="switch_accent_color">#ff7fcac3</color>
<!-- Keyboard shortcuts colors -->
- <color name="ksh_system_group_color">#ff00bcd4</color>
+ <color name="ksh_system_group_color">@color/material_deep_teal_500</color>
<color name="ksh_application_group_color">#fff44336</color>
<color name="ksh_keyword_color">#d9000000</color>
<color name="ksh_key_item_color">@color/material_grey_600</color>
- <color name="ksh_key_item_background">#eeeeee</color>
+ <color name="ksh_key_item_background">@color/material_grey_100</color>
<!-- Background color of edit overflow -->
<color name="qs_edit_overflow_bg">#455A64</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a33b7a3..dc9ffa9 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -735,10 +735,8 @@
<string name="recents_launch_disabled_message"><xliff:g id="app" example="Calendar">%s</xliff:g> is disabled in safe-mode.</string>
<!-- Recents: Stack action button string. [CHAR LIMIT=NONE] -->
<string name="recents_stack_action_button_label">Clear all</string>
- <!-- Recents: Non-dockable task drag message. [CHAR LIMIT=NONE] -->
- <string name="recents_drag_non_dockable_task_message">This app does not support multi-window</string>
- <!-- Recents: Non-dockable task launch sub header. [CHAR LIMIT=NONE] -->
- <string name="recents_launch_non_dockable_task_label">App does not support multi-window</string>
+ <!-- Recents: Incompatible task message. [CHAR LIMIT=NONE] -->
+ <string name="recents_incompatible_app_message">App doesn\'t support split screen</string>
<!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
<string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
@@ -1594,4 +1592,7 @@
<!-- Warning message when we try to dock a non-resizeble tasks and launch it in fullscreen instead. -->
<string name="dock_non_resizeble_failed_to_dock_text">App does not support split-screen.</string>
+ <!-- accessibility label for button to expand quick settings [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_expand">Expand quick settings.</string>
+
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f560a13..0730083 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -308,6 +308,11 @@
<item name="android:layout_gravity">center_vertical</item>
</style>
+ <style name="DockedDividerMinimizedShadow">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">8dp</item>
+ </style>
+
<style name="DockedDividerHandle">
<item name="android:layout_gravity">center_horizontal</item>
<item name="android:layout_width">96dp</item>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index 087f61e..076b5bc 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -37,7 +37,7 @@
import com.android.systemui.statusbar.policy.BatteryController;
-public class BatteryMeterDrawable extends Drawable implements DemoMode,
+public class BatteryMeterDrawable extends Drawable implements
BatteryController.BatteryStateChangeCallback {
private static final float ASPECT_RATIO = 9.5f / 14.5f;
@@ -184,14 +184,12 @@
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver);
updateShowPercent();
- if (mDemoMode) return;
mBatteryController.addStateChangedCallback(this);
}
public void stopListening() {
mListening = false;
mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
- if (mDemoMode) return;
mBatteryController.removeStateChangedCallback(this);
}
@@ -507,35 +505,6 @@
return 0;
}
- private boolean mDemoMode;
-
- @Override
- public void dispatchDemoCommand(String command, Bundle args) {
- if (!mDemoMode && command.equals(COMMAND_ENTER)) {
- mBatteryController.removeStateChangedCallback(this);
- mDemoMode = true;
- if (mListening) {
- mBatteryController.removeStateChangedCallback(this);
- }
- } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
- mDemoMode = false;
- postInvalidate();
- if (mListening) {
- mBatteryController.addStateChangedCallback(this);
- }
- } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {
- String level = args.getString("level");
- String plugged = args.getString("plugged");
- if (level != null) {
- mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100);
- }
- if (plugged != null) {
- mPluggedIn = Boolean.parseBoolean(plugged);
- }
- postInvalidate();
- }
- }
-
private final class SettingObserver extends ContentObserver {
public SettingObserver() {
super(new Handler());
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index 001d1f2..73b9d02 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -32,7 +32,8 @@
/**
* Docks the top-most task and opens recents.
*/
- boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds);
+ boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds,
+ int metricsDockAction);
/**
* Called during a drag-from-navbar-in gesture.
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 455a69f..39a3412 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -63,7 +63,8 @@
* above.
*/
private final Class<?>[] SERVICES_PER_USER = new Class[] {
- com.android.systemui.recents.Recents.class
+ com.android.systemui.recents.Recents.class,
+ com.android.systemui.tv.pip.PipUI.class
};
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 66754a7..f892fd6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -847,17 +847,16 @@
private void doKeyguardLaterForChildProfilesLocked() {
UserManager um = UserManager.get(mContext);
- List<UserInfo> profiles = um.getEnabledProfiles(UserHandle.myUserId());
- for (UserInfo info : profiles) {
- if (mLockPatternUtils.isSeparateProfileChallengeEnabled(info.id)) {
- long userTimeout = getLockTimeout(info.id);
+ for (int profileId : um.getEnabledProfileIds(UserHandle.myUserId())) {
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(profileId)) {
+ long userTimeout = getLockTimeout(profileId);
if (userTimeout == 0) {
doKeyguardForChildProfilesLocked();
} else {
long userWhen = SystemClock.elapsedRealtime() + userTimeout;
Intent lockIntent = new Intent(DELAYED_LOCK_PROFILE_ACTION);
lockIntent.putExtra("seq", mDelayedProfileShowingSequence);
- lockIntent.putExtra(Intent.EXTRA_USER_ID, info.id);
+ lockIntent.putExtra(Intent.EXTRA_USER_ID, profileId);
PendingIntent lockSender = PendingIntent.getBroadcast(
mContext, 0, lockIntent, PendingIntent.FLAG_CANCEL_CURRENT);
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
@@ -869,10 +868,9 @@
private void doKeyguardForChildProfilesLocked() {
UserManager um = UserManager.get(mContext);
- List<UserInfo> profiles = um.getEnabledProfiles(UserHandle.myUserId());
- for (UserInfo info : profiles) {
- if (mLockPatternUtils.isSeparateProfileChallengeEnabled(info.id)) {
- lockProfile(info.id);
+ for (int profileId : um.getEnabledProfileIds(UserHandle.myUserId())) {
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(profileId)) {
+ lockProfile(profileId);
}
}
}
@@ -1482,9 +1480,8 @@
final UserHandle currentUser = new UserHandle(KeyguardUpdateMonitor.getCurrentUser());
final UserManager um = (UserManager) mContext.getSystemService(
Context.USER_SERVICE);
- List <UserInfo> userHandles = um.getProfiles(currentUser.getIdentifier());
- for (UserInfo ui : userHandles) {
- mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, ui.getUserHandle());
+ for (int profileId : um.getProfileIdsWithDisabled(currentUser.getIdentifier())) {
+ mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, UserHandle.of(profileId));
}
} else {
mBootSendUserPresent = true;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
index ba07bf4..5cb46ac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -118,6 +118,10 @@
}
ImageView first = (ImageView) getChildAt(firstIndex);
ImageView second = (ImageView) getChildAt(secondIndex);
+ if (second == null) {
+ // Weird state where number of pages must not have propagated yet.
+ return;
+ }
// Lay the two views on top of each other.
second.setTranslationX(first.getX() - second.getX());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 4d959d8..af81c19 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -87,6 +87,7 @@
public void setOnKeyguard(boolean onKeyguard) {
mOnKeyguard = onKeyguard;
+ mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE);
if (mOnKeyguard) {
clearAnimationState();
}
@@ -290,7 +291,7 @@
@Override
public void onAnimationStarted() {
- mQuickQsPanel.setVisibility(View.VISIBLE);
+ mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE);
if (mOnFirstPage) {
final int N = mTopFiveQs.size();
for (int i = 0; i < N; i++) {
@@ -302,12 +303,11 @@
private void clearAnimationState() {
final int N = mAllViews.size();
mQuickQsPanel.setAlpha(0);
- mQuickQsPanel.setVisibility(View.VISIBLE);
for (int i = 0; i < N; i++) {
View v = mAllViews.get(i);
v.setAlpha(1);
- v.setTranslationX(1);
- v.setTranslationY(1);
+ v.setTranslationX(0);
+ v.setTranslationY(0);
}
final int N2 = mTopFiveQs.size();
for (int i = 0; i < N2; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
index e3a4909..ef75562 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
@@ -70,8 +70,8 @@
super.onFinishInflate();
mQSPanel = (QSPanel) findViewById(R.id.quick_settings_panel);
mQSDetail = (QSDetail) findViewById(R.id.qs_detail);
- mQSDetail.setQsPanel(mQSPanel);
mHeader = (BaseStatusBarHeader) findViewById(R.id.header);
+ mQSDetail.setQsPanel(mQSPanel, mHeader);
mQSAnimator = new QSAnimator(this, (QuickQSPanel) mHeader.findViewById(R.id.quick_qs_panel),
mQSPanel);
mQSCustomizer = (QSCustomizer) findViewById(R.id.qs_customize);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 50c0cca..0cf7e479 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -35,6 +35,7 @@
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile.DetailAdapter;
+import com.android.systemui.statusbar.phone.BaseStatusBarHeader;
import com.android.systemui.statusbar.phone.QSTileHost;
public class QSDetail extends LinearLayout {
@@ -62,6 +63,7 @@
private boolean mClosingDetail;
private boolean mFullyExpanded;
private View mQsDetailHeaderBack;
+ private BaseStatusBarHeader mHeader;
public QSDetail(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -107,8 +109,9 @@
mDetailDoneButton.setOnClickListener(doneListener);
}
- public void setQsPanel(QSPanel panel) {
+ public void setQsPanel(QSPanel panel, BaseStatusBarHeader header) {
mQsPanel = panel;
+ mHeader = header;
mQsPanel.setCallback(mQsPanelCallback);
}
@@ -195,6 +198,7 @@
mClosingDetail = true;
mDetailAdapter = null;
listener = mTeardownDetailWhenDone;
+ mHeader.setVisibility(View.VISIBLE);
mQsPanel.setGridContentVisibility(true);
mQsPanelCallback.onScanStateChanged(false);
}
@@ -273,6 +277,7 @@
// Only hide content if still in detail state.
if (mDetailAdapter != null) {
mQsPanel.setGridContentVisibility(false);
+ mHeader.setVisibility(View.INVISIBLE);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
index 9e40cfd..44b38f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
@@ -93,6 +93,12 @@
}
}
+ @Override
+ public boolean hasOverlappingRendering() {
+ // Avoid layers for this layout - we don't need them.
+ return false;
+ }
+
/**
* Update the accessibility order for this view.
*
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index ab21532..f34df75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -16,13 +16,18 @@
package com.android.systemui.qs.external;
import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
import android.os.Handler;
import android.os.UserHandle;
import android.service.quicksettings.IQSTileService;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
+import libcore.util.Objects;
/**
* Manages the priority which lets {@link TileServices} make decisions about which tiles
@@ -68,6 +73,12 @@
mHandler = handler;
mStateManager = tileLifecycleManager;
mStateManager.setQSService(tileServices);
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ mServices.getContext().registerReceiverAsUser(mUninstallReceiver,
+ new UserHandle(ActivityManager.getCurrentUser()), filter, null, mHandler);
}
public boolean isActiveTile() {
@@ -106,6 +117,7 @@
}
public void handleDestroy() {
+ mServices.getContext().unregisterReceiver(mUninstallReceiver);
mStateManager.handleDestroy();
}
@@ -198,4 +210,23 @@
mServices.recalculateBindAllowance();
}
};
+
+ private final BroadcastReceiver mUninstallReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+ return;
+ }
+ if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ return;
+ }
+ Uri data = intent.getData();
+ String pkgName = data.getEncodedSchemeSpecificPart();
+ final ComponentName component = mStateManager.getComponent();
+ if (!Objects.equal(pkgName, component.getPackageName())) {
+ return;
+ }
+ mServices.getHost().removeTile(component);
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index f36d013..2ab6b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -72,6 +72,10 @@
return mContext;
}
+ public QSTileHost getHost() {
+ return mHost;
+ }
+
public TileServiceManager getTileWrapper(CustomTile tile) {
ComponentName component = tile.getComponent();
TileServiceManager service = onCreateTileService(component);
@@ -89,6 +93,7 @@
public void freeService(CustomTile tile, TileServiceManager service) {
synchronized (mServices) {
service.setBindAllowed(false);
+ service.handleDestroy();
mServices.remove(tile);
mTiles.remove(tile.getComponent());
final String slot = tile.getComponent().getClassName();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index e494fd8..77eaa3b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -130,6 +130,8 @@
}
};
state.label = percentage;
+ state.contentDescription = mContext.getString(R.string.accessibility_quick_settings_battery,
+ percentage);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 287bb22..b2d7b48 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
@@ -36,10 +37,13 @@
import android.provider.Settings;
import android.util.EventLog;
import android.util.Log;
+import android.util.MutableBoolean;
import android.view.Display;
import android.view.View;
import android.widget.Toast;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
@@ -78,6 +82,10 @@
private final static String ACTION_HIDE_RECENTS = "com.android.systemui.recents.ACTION_HIDE";
private final static String ACTION_TOGGLE_RECENTS = "com.android.systemui.recents.ACTION_TOGGLE";
+ private static final String COUNTER_WINDOW_SUPPORTED = "window_enter_supported";
+ private static final String COUNTER_WINDOW_UNSUPPORTED = "window_enter_unsupported";
+ private static final String COUNTER_WINDOW_INCOMPATIBLE = "window_enter_incompatible";
+
private static SystemServicesProxy sSystemServicesProxy;
private static RecentsDebugFlags sDebugFlags;
private static RecentsTaskLoader sTaskLoader;
@@ -393,7 +401,8 @@
}
@Override
- public boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds) {
+ public boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds,
+ int metricsDockAction) {
// Ensure the device has been provisioned before allowing the user to interact with
// recents
if (!isUserSetup()) {
@@ -413,7 +422,12 @@
boolean screenPinningActive = ssp.isScreenPinningActive();
boolean isTopTaskHome = topTask != null && SystemServicesProxy.isHomeStack(topTask.stackId);
if (topTask != null && !isTopTaskHome && !screenPinningActive) {
+ logDockAttempt(mContext, topTask.topActivity, topTask.resizeMode);
if (topTask.isDockable) {
+ if (metricsDockAction != -1) {
+ MetricsLogger.action(mContext, metricsDockAction,
+ topTask.topActivity.flattenToShortString());
+ }
if (sSystemServicesProxy.isSystemUser(currentUser)) {
mImpl.dockTopTask(topTask.id, dragMode, stackCreateMode, initialBounds);
} else {
@@ -435,7 +449,7 @@
mDraggingInRecentsCurrentUser = currentUser;
return true;
} else {
- Toast.makeText(mContext, R.string.recents_drag_non_dockable_task_message,
+ Toast.makeText(mContext, R.string.recents_incompatible_app_message,
Toast.LENGTH_SHORT).show();
return false;
}
@@ -444,6 +458,26 @@
}
}
+ public static void logDockAttempt(Context ctx, ComponentName activity, int resizeMode) {
+ if (resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE) {
+ MetricsLogger.action(ctx, MetricsEvent.ACTION_WINDOW_DOCK_UNRESIZABLE,
+ activity.flattenToShortString());
+ }
+ MetricsLogger.count(ctx, getMetricsCounterForResizeMode(resizeMode), 1);
+ }
+
+ private static String getMetricsCounterForResizeMode(int resizeMode) {
+ switch (resizeMode) {
+ case ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE:
+ return COUNTER_WINDOW_UNSUPPORTED;
+ case ActivityInfo.RESIZE_MODE_RESIZEABLE:
+ case ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE:
+ return COUNTER_WINDOW_SUPPORTED;
+ default:
+ return COUNTER_WINDOW_INCOMPATIBLE;
+ }
+ }
+
@Override
public void onDraggingInRecents(float distanceFromTop) {
if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index b1d9555..6b476ee 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -38,6 +38,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
@@ -58,8 +59,10 @@
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
+import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
+import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
import com.android.systemui.recents.events.ui.UserInteractionEvent;
@@ -68,12 +71,12 @@
import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsPackageMonitor;
import com.android.systemui.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.AnimationProps;
import com.android.systemui.recents.views.RecentsView;
import com.android.systemui.recents.views.SystemBarScrimViews;
import com.android.systemui.statusbar.BaseStatusBar;
@@ -90,6 +93,7 @@
private final static boolean DEBUG = false;
public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
+ public final static int INCOMPATIBLE_APP_ALPHA_DURATION = 150;
private RecentsPackageMonitor mPackageMonitor;
private long mLastTabKeyEventTime;
@@ -101,6 +105,7 @@
// Top level views
private RecentsView mRecentsView;
private SystemBarScrimViews mScrimViews;
+ private View mIncompatibleAppOverlay;
// Runnables to finish the Recents activity
private Intent mHomeIntent;
@@ -674,6 +679,30 @@
MetricsLogger.count(this, "overview_app_info", 1);
}
+ public final void onBusEvent(ShowIncompatibleAppOverlayEvent event) {
+ if (mIncompatibleAppOverlay == null) {
+ mIncompatibleAppOverlay = Utilities.findViewStubById(this,
+ R.id.incompatible_app_overlay_stub).inflate();
+ mIncompatibleAppOverlay.setWillNotDraw(false);
+ mIncompatibleAppOverlay.setVisibility(View.VISIBLE);
+ }
+ mIncompatibleAppOverlay.animate()
+ .alpha(1f)
+ .setDuration(INCOMPATIBLE_APP_ALPHA_DURATION)
+ .setInterpolator(Interpolators.ALPHA_IN)
+ .start();
+ }
+
+ public final void onBusEvent(HideIncompatibleAppOverlayEvent event) {
+ if (mIncompatibleAppOverlay != null) {
+ mIncompatibleAppOverlay.animate()
+ .alpha(0f)
+ .setDuration(INCOMPATIBLE_APP_ALPHA_DURATION)
+ .setInterpolator(Interpolators.ALPHA_OUT)
+ .start();
+ }
+ }
+
public final void onBusEvent(DeleteTaskDataEvent event) {
// Remove any stored data from the loader
RecentsTaskLoader loader = Recents.getTaskLoader();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java
new file mode 100644
index 0000000..d6ef636
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.ui;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when a user stops draggin an incompatible app task.
+ */
+public class HideIncompatibleAppOverlayEvent extends EventBus.Event {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java
new file mode 100644
index 0000000..3a4350e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.ui;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when a user starts dragging an incompatible app task.
+ */
+public class ShowIncompatibleAppOverlayEvent extends EventBus.Event {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 69d98af..4ecda54 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -19,17 +19,20 @@
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.annotation.FloatRange;
+import android.app.Activity;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
+import android.os.Trace;
import android.util.ArraySet;
import android.util.IntProperty;
import android.util.Property;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewParent;
+import android.view.ViewStub;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.views.TaskViewTransform;
@@ -220,6 +223,20 @@
}
/**
+ * Returns a view stub for the given view id.
+ */
+ public static ViewStub findViewStubById(View v, int stubId) {
+ return (ViewStub) v.findViewById(stubId);
+ }
+
+ /**
+ * Returns a view stub for the given view id.
+ */
+ public static ViewStub findViewStubById(Activity a, int stubId) {
+ return (ViewStub) a.findViewById(stubId);
+ }
+
+ /**
* Updates {@param transforms} to be the same size as {@param tasks}.
*/
public static void matchTaskListSize(List<Task> tasks, List<TaskViewTransform> transforms) {
@@ -245,6 +262,14 @@
}
/**
+ * Adds a trace event for debugging.
+ */
+ public static void addTraceEvent(String event) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, event);
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+
+ /**
* Returns a lightweight dump of a rect.
*/
public static String dumpRect(Rect r) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 7aeff1f..af1628b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -206,7 +206,7 @@
Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
thumbnail, title, titleDescription, dismissDescription, appInfoDescription,
activityColor, backgroundColor, isLaunchTarget, isStackTask, isSystemApp,
- t.isDockable, t.bounds, t.taskDescription);
+ t.isDockable, t.bounds, t.taskDescription, t.resizeMode, t.topActivity);
allTasks.add(task);
affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 24eeaf2..9f9c48f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -19,6 +19,7 @@
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Rect;
@@ -174,6 +175,15 @@
@ViewDebug.ExportedProperty(category="recents")
public boolean isDockable;
+ /**
+ * Resize mode. See {@link ActivityInfo#resizeMode}.
+ */
+ @ViewDebug.ExportedProperty(category="recents")
+ public int resizeMode;
+
+ @ViewDebug.ExportedProperty(category="recents")
+ public ComponentName topActivity;
+
private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
public Task() {
@@ -184,7 +194,8 @@
Bitmap thumbnail, String title, String titleDescription, String dismissDescription,
String appInfoDescription, int colorPrimary, int colorBackground,
boolean isLaunchTarget, boolean isStackTask, boolean isSystemApp,
- boolean isDockable, Rect bounds, ActivityManager.TaskDescription taskDescription) {
+ boolean isDockable, Rect bounds, ActivityManager.TaskDescription taskDescription,
+ int resizeMode, ComponentName topActivity) {
boolean isInAffiliationGroup = (affiliationTaskId != key.id);
boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
this.key = key;
@@ -206,6 +217,8 @@
this.isStackTask = isStackTask;
this.isSystemApp = isSystemApp;
this.isDockable = isDockable;
+ this.resizeMode = resizeMode;
+ this.topActivity = topActivity;
}
/**
@@ -231,6 +244,8 @@
this.isStackTask = o.isStackTask;
this.isSystemApp = o.isSystemApp;
this.isDockable = o.isDockable;
+ this.resizeMode = o.resizeMode;
+ this.topActivity = o.topActivity;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index 13e1a14..60a85df 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -55,6 +55,7 @@
import com.android.systemui.recents.tv.views.RecentsTvView;
import com.android.systemui.recents.tv.views.TaskStackHorizontalGridView;
import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
+import com.android.systemui.recents.views.AnimationProps;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.tv.pip.PipManager;
import com.android.systemui.tv.pip.PipRecentsOverlayManager;
@@ -246,7 +247,7 @@
dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable);
dismissEvent.addPostAnimationCallback(closeSystemWindows);
- if(mTaskStackHorizontalGridView.getChildCount() > 0) {
+ if(mTaskStackHorizontalGridView.getChildCount() > 0 && animateTaskViews) {
mHomeRecentsEnterExitAnimationHolder.startExitAnimation(dismissEvent);
} else {
closeSystemWindows.run();
@@ -343,6 +344,13 @@
} else {
mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
}
+ if(mTaskStackHorizontalGridView.getStack().getTaskCount() > 1 && !mLaunchedFromHome) {
+ // If there are 2 or more tasks, and we are not launching from home
+ // set the selected position to the 2nd task to allow for faster app switching
+ mTaskStackHorizontalGridView.setSelectedPosition(1);
+ } else {
+ mTaskStackHorizontalGridView.setSelectedPosition(0);
+ }
// If this is a new instance from a configuration change, then we have to manually trigger
// the enter animation state, or if recents was relaunched by AM, without going through
@@ -494,12 +502,10 @@
}
public final void onBusEvent(AllTaskViewsDismissedEvent event) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.hasDockedTask()) {
+ if (mPipManager.isPipShown()) {
mRecentsView.showEmptyView();
} else {
- // Just go straight home (no animation necessary because there are no more task views)
- dismissRecentsToHome(false /* animateTaskViews */);
+ dismissRecentsToHome(false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
index fd31872..dc0d1f1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
@@ -75,7 +75,8 @@
if (useThumbnailTransition) {
// Try starting with a thumbnail transition
- ActivityOptions opts = getThumbnailTransitionActivityOptionsForTV(topTask);
+ ActivityOptions opts = getThumbnailTransitionActivityOptionsForTV(topTask,
+ stack.getTaskCount());
if (opts != null) {
startRecentsActivity(topTask, opts, false /* fromHome */, true /* fromThumbnail */);
} else {
@@ -118,8 +119,8 @@
* Creates the activity options for an app->recents transition on TV.
*/
private ActivityOptions getThumbnailTransitionActivityOptionsForTV(
- ActivityManager.RunningTaskInfo topTask) {
- Rect rect = TaskCardView.getStartingCardThumbnailRect(mContext);
+ ActivityManager.RunningTaskInfo topTask, int numTasks) {
+ Rect rect = TaskCardView.getStartingCardThumbnailRect(mContext, numTasks);
SystemServicesProxy ssp = Recents.getSystemServices();
ThumbnailData thumbnailData = ssp.getTaskThumbnail(topTask.id);
if (thumbnailData.thumbnail != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java
index fbcfa97..3e668af 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java
@@ -26,13 +26,13 @@
public class DismissAnimationsHolder {
private LinearLayout mDismissArea;
- private LinearLayout mTaskCardView;
+ private LinearLayout mRecentsTvCard;
private int mCardYDelta;
private long mShortDuration;
private long mLongDuration;
public DismissAnimationsHolder(TaskCardView taskCardView) {
- mTaskCardView = (LinearLayout) taskCardView.findViewById(R.id.recents_tv_card);
+ mRecentsTvCard = (LinearLayout) taskCardView.findViewById(R.id.recents_tv_card);
mDismissArea = (LinearLayout) taskCardView.findViewById(R.id.card_dismiss);
Resources res = taskCardView.getResources();
@@ -47,7 +47,7 @@
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.alpha(1.0f);
- mTaskCardView.animate()
+ mRecentsTvCard.animate()
.setDuration(mShortDuration)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.translationYBy(mCardYDelta)
@@ -60,7 +60,7 @@
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.alpha(0.0f);
- mTaskCardView.animate()
+ mRecentsTvCard.animate()
.setDuration(mShortDuration)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.translationYBy(-mCardYDelta)
@@ -73,11 +73,17 @@
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.alpha(0.0f);
- mTaskCardView.animate()
+ mRecentsTvCard.animate()
.setDuration(mLongDuration)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.translationYBy(mCardYDelta)
.alpha(0.0f)
.setListener(listener);
}
+
+ public void reset() {
+ mRecentsTvCard.setAlpha(1.0f);
+ mRecentsTvCard.setTranslationY(0);
+ mRecentsTvCard.animate().setListener(null);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
index 53fdf62..b876fc70 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
@@ -250,8 +250,9 @@
public TaskStackHorizontalGridView setTaskStackViewAdapter(
TaskStackHorizontalViewAdapter taskStackViewAdapter) {
- if(mTaskStackHorizontalView != null) {
+ if (mTaskStackHorizontalView != null) {
mTaskStackHorizontalView.setAdapter(taskStackViewAdapter);
+ taskStackViewAdapter.setTaskStackHorizontalGridView(mTaskStackHorizontalView);
}
return mTaskStackHorizontalView;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
index 99d478b..d3bc4b6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
@@ -95,7 +95,38 @@
return r;
}
- public static Rect getStartingCardThumbnailRect(Context context) {
+ public static Rect getStartingCardThumbnailRect(Context context, int numberOfTasks) {
+ if(numberOfTasks > 1) {
+ return getStartingCardThumbnailRectForStartPosition(context);
+ } else {
+ return getStartingCardThumbnailRectForFocusedPosition(context);
+ }
+ }
+
+ private static Rect getStartingCardThumbnailRectForStartPosition(Context context) {
+ Resources res = context.getResources();
+
+ int width = res.getDimensionPixelOffset(R.dimen.recents_tv_card_width);
+ int totalSpacing = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_card_spacing) * 2
+ + res.getDimensionPixelOffset(R.dimen.recents_tv_gird_focused_card_delta);
+ int height = res.getDimensionPixelOffset(R.dimen.recents_tv_screenshot_height);
+ int topMargin = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_row_top_margin);
+ int headerHeight = res.getDimensionPixelOffset(R.dimen.recents_tv_card_extra_badge_size) +
+ res.getDimensionPixelOffset(R.dimen.recents_tv_icon_padding_bottom);
+
+ WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ Display display = wm.getDefaultDisplay();
+ Point size = new Point();
+ display.getSize(size);
+ int screenWidth = size.x;
+
+ return new Rect(screenWidth / 2 + width / 2 + totalSpacing,
+ topMargin + headerHeight,
+ screenWidth / 2 + width / 2 + totalSpacing + width,
+ topMargin + headerHeight + height);
+ }
+
+ private static Rect getStartingCardThumbnailRectForFocusedPosition(Context context) {
Resources res = context.getResources();
TypedValue out = new TypedValue();
@@ -127,7 +158,6 @@
Point size = new Point();
display.getSize(size);
int screenWidth = size.x;
- int screenHeight = size.y;
return new Rect(screenWidth / 2 - width / 2 - widthDelta / 2,
topMargin - totalHeightDelta / 2 + (int) (headerHeight * scale),
@@ -189,6 +219,7 @@
}
public void startDismissTaskAnimation(Animator.AnimatorListener listener) {
+ mDismissState = false;
mDismissAnimationsHolder.startDismissAnimation(listener);
}
@@ -201,4 +232,10 @@
super.onDetachedFromWindow();
setDismissState(false);
}
+
+ public void reset() {
+ mDismissState = false;
+ mRecentsRowFocusAnimationHolder.reset();
+ mDismissAnimationsHolder.reset();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
index 603721a..77ab8c1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
@@ -179,13 +179,14 @@
@Override
public void onStackTaskAdded(TaskStack stack, Task newTask) {
- getAdapter().notifyItemInserted(stack.getStackTasks().indexOf(newTask));
+ ((TaskStackHorizontalViewAdapter) getAdapter()).addTaskAt(newTask,
+ stack.indexOfStackTask(newTask));
}
@Override
public void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask,
Task newFrontMostTask, AnimationProps animation, boolean fromDockGesture) {
- getAdapter().notifyItemRemoved(stack.getStackTasks().indexOf(removedTask));
+ ((TaskStackHorizontalViewAdapter) getAdapter()).removeTask(removedTask);
if (mFocusedTask == removedTask) {
resetFocusedTask(removedTask);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
index 97712ea..eff1845 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
@@ -27,7 +27,9 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.LaunchTvTaskEvent;
import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
+import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.views.AnimationProps;
import java.util.ArrayList;
import java.util.List;
@@ -40,6 +42,7 @@
//Full class name is 30 characters
private static final String TAG = "TaskStackViewAdapter";
private List<Task> mTaskList;
+ private TaskStackHorizontalGridView mGridView;
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private TaskCardView mTaskCardView;
@@ -62,7 +65,7 @@
try {
if (mTaskCardView.isInDismissState()) {
mTaskCardView.startDismissTaskAnimation(
- getRemoveAtListener(getAdapterPosition(), mTaskCardView));
+ getRemoveAtListener(getAdapterPosition(), mTaskCardView.getTask()));
} else {
EventBus.getDefault().send(new LaunchTvTaskEvent(mTaskCardView, mTask,
null, INVALID_STACK_ID));
@@ -74,6 +77,28 @@
}
}
+
+ private Animator.AnimatorListener getRemoveAtListener(final int position,
+ final Task task) {
+ return new Animator.AnimatorListener() {
+
+ @Override
+ public void onAnimationStart(Animator animation) { }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ removeAt(position);
+ EventBus.getDefault().send(new DeleteTaskDataEvent(task));
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) { }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) { }
+ };
+
+ }
}
public TaskStackHorizontalViewAdapter(List tasks) {
@@ -101,39 +126,43 @@
}
@Override
+ public void onViewDetachedFromWindow(ViewHolder holder) {
+ holder.mTaskCardView.reset();
+ }
+
+ @Override
public int getItemCount() {
return mTaskList.size();
}
- private Animator.AnimatorListener getRemoveAtListener(final int position,
- final TaskCardView taskCardView) {
- return new Animator.AnimatorListener() {
-
- @Override
- public void onAnimationStart(Animator animation) { }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- removeAt(position);
- EventBus.getDefault().send(new DeleteTaskDataEvent(taskCardView.getTask()));
- }
-
- @Override
- public void onAnimationCancel(Animator animation) { }
-
- @Override
- public void onAnimationRepeat(Animator animation) { }
- };
-
+ private void removeAt(int position) {
+ Task removedTask = mTaskList.remove(position);
+ if (mGridView != null) {
+ mGridView.getStack().removeTask(removedTask, AnimationProps.IMMEDIATE,
+ false);
+ }
+ notifyItemRemoved(position);
}
- private void removeAt(int position) {
- mTaskList.remove(position);
- notifyItemRemoved(position);
+ public void removeTask(Task task) {
+ int position = mTaskList.indexOf(task);
+ if (position >= 0) {
+ mTaskList.remove(position);
+ notifyItemRemoved(position);
+ }
}
public int getPositionOfTask(Task task) {
int position = mTaskList.indexOf(task);
return (position >= 0) ? position : 0;
}
+
+ public void setTaskStackHorizontalGridView(TaskStackHorizontalGridView gridView) {
+ mGridView = gridView;
+ }
+
+ public void addTaskAt(Task task, int position) {
+ mTaskList.add(position, task);
+ notifyItemInserted(position);
+ }
}
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 21a43d5..59b7560 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -540,7 +540,8 @@
mTransitionHelper.wrapStartedListener(startedListener),
true /* scaleUp */);
- MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP);
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP,
+ event.task.topActivity.flattenToShortString());
} else {
// Animate the overlay alpha back to 0
updateVisibleDockRegions(null, true /* isDefaultDockState */, -1,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index a867bde..5fbc037 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -20,19 +20,17 @@
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
-import android.provider.Settings;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
-import android.widget.Toast;
import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsImpl;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
+import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
+import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
@@ -43,7 +41,6 @@
import java.util.ArrayList;
-
/**
* Represents the dock regions for each orientation.
*/
@@ -165,9 +162,9 @@
mVisibleDockStates.clear();
if (ActivityManager.supportsMultiWindow() && !ssp.hasDockedTask()
&& mDividerSnapAlgorithm.isSplitScreenFeasible()) {
+ Recents.logDockAttempt(mRv.getContext(), event.task.topActivity, event.task.resizeMode);
if (!event.task.isDockable) {
- Toast.makeText(mRv.getContext(), R.string.recents_drag_non_dockable_task_message,
- Toast.LENGTH_SHORT).show();
+ EventBus.getDefault().send(new ShowIncompatibleAppOverlayEvent());
} else {
// Add the dock state drop targets (these take priority)
TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation();
@@ -184,6 +181,9 @@
}
public final void onBusEvent(DragEndEvent event) {
+ if (!mDragTask.isDockable) {
+ EventBus.getDefault().send(new HideIncompatibleAppOverlayEvent());
+ }
mDragRequested = false;
mDragTask = null;
mTaskView = null;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index b75a91e..e4da8b3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -23,13 +23,13 @@
import android.graphics.Path;
import android.graphics.Rect;
import android.util.ArraySet;
+import android.util.MutableFloat;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.ViewDebug;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.RecentsDebugFlags;
@@ -628,22 +628,24 @@
/**
* Updates this stack when a scroll happens.
+ *
*/
- public void updateFocusStateOnScroll(float stackScroll, float deltaScroll) {
- if (deltaScroll == 0f) {
- return;
+ public float updateFocusStateOnScroll(float lastTargetStackScroll, float targetStackScroll,
+ float lastStackScroll) {
+ if (targetStackScroll == lastStackScroll) {
+ return targetStackScroll;
}
+ float deltaScroll = targetStackScroll - lastStackScroll;
+ float deltaTargetScroll = targetStackScroll - lastTargetStackScroll;
+ float newScroll = targetStackScroll;
+ mUnfocusedRange.offset(targetStackScroll);
for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) {
int taskId = mTaskIndexOverrideMap.keyAt(i);
float x = mTaskIndexMap.get(taskId);
float overrideX = mTaskIndexOverrideMap.get(taskId, 0f);
float newOverrideX = overrideX + deltaScroll;
- mUnfocusedRange.offset(stackScroll);
- boolean outOfBounds = mUnfocusedRange.getNormalizedX(newOverrideX) < 0f ||
- mUnfocusedRange.getNormalizedX(newOverrideX) > 1f;
- if (outOfBounds || (overrideX >= x && x >= newOverrideX) ||
- (overrideX <= x && x <= newOverrideX)) {
+ if (isInvalidOverrideX(x, overrideX, newOverrideX)) {
// Remove the override once we reach the original task index
mTaskIndexOverrideMap.removeAt(i);
} else if ((overrideX >= x && deltaScroll <= 0f) ||
@@ -652,11 +654,23 @@
mTaskIndexOverrideMap.put(taskId, newOverrideX);
} else {
// Scrolling override x away from x, we should still move the scroll towards x
- float deltaX = overrideX - x;
- newOverrideX = Math.signum(deltaX) * (Math.abs(deltaX) - Math.abs(deltaScroll));
- mTaskIndexOverrideMap.put(taskId, x + newOverrideX);
+ newScroll = lastStackScroll;
+ newOverrideX = overrideX - deltaTargetScroll;
+ if (isInvalidOverrideX(x, overrideX, newOverrideX)) {
+ mTaskIndexOverrideMap.removeAt(i);
+ } else{
+ mTaskIndexOverrideMap.put(taskId, newOverrideX);
+ }
}
}
+ return newScroll;
+ }
+
+ private boolean isInvalidOverrideX(float x, float overrideX, float newOverrideX) {
+ boolean outOfBounds = mUnfocusedRange.getNormalizedX(newOverrideX) < 0f ||
+ mUnfocusedRange.getNormalizedX(newOverrideX) > 1f;
+ return outOfBounds || (overrideX >= x && x >= newOverrideX) ||
+ (overrideX <= x && x <= newOverrideX);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 13c8403..0fc45ed 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -1618,7 +1618,6 @@
if (animation != null) {
relayoutTaskViewsOnNextFrame(animation);
}
- mLayoutAlgorithm.updateFocusStateOnScroll(curScroll, curScroll - prevScroll);
if (mEnterAnimationComplete) {
if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index 19b3c94..1fa73c6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.util.FloatProperty;
import android.util.Log;
+import android.util.MutableFloat;
import android.util.Property;
import android.view.ViewDebug;
import android.widget.OverScroller;
@@ -66,6 +67,8 @@
@ViewDebug.ExportedProperty(category="recents")
float mStackScrollP;
+ @ViewDebug.ExportedProperty(category="recents")
+ float mLastDeltaP = 0f;
float mFlingDownScrollP;
int mFlingDownY;
@@ -84,6 +87,11 @@
/** Resets the task scroller. */
void reset() {
mStackScrollP = 0f;
+ mLastDeltaP = 0f;
+ }
+
+ void resetDeltaScroll() {
+ mLastDeltaP = 0f;
}
/** Gets the current stack scroll */
@@ -99,14 +107,27 @@
}
/**
+ * Sets the current stack scroll immediately, and returns the difference between the target
+ * scroll and the actual scroll after accounting for the effect on the focus state.
+ */
+ public float setDeltaStackScroll(float downP, float deltaP) {
+ float targetScroll = downP + deltaP;
+ float newScroll = mLayoutAlgorithm.updateFocusStateOnScroll(downP + mLastDeltaP, targetScroll,
+ mStackScrollP);
+ setStackScroll(newScroll, AnimationProps.IMMEDIATE);
+ mLastDeltaP = deltaP;
+ return newScroll - targetScroll;
+ }
+
+ /**
* Sets the current stack scroll, but indicates to the callback the preferred animation to
* update to this new scroll.
*/
- public void setStackScroll(float s, AnimationProps animation) {
- float prevStackScroll = mStackScrollP;
- mStackScrollP = s;
+ public void setStackScroll(float newScroll, AnimationProps animation) {
+ float prevScroll = mStackScrollP;
+ mStackScrollP = newScroll;
if (mCb != null) {
- mCb.onStackScrollChanged(prevStackScroll, mStackScrollP, animation);
+ mCb.onStackScrollChanged(prevScroll, mStackScrollP, animation);
}
}
@@ -115,9 +136,9 @@
* @return whether the stack progress changed.
*/
public boolean setStackScrollToInitialState() {
- float prevStackScrollP = mStackScrollP;
+ float prevScroll = mStackScrollP;
setStackScroll(mLayoutAlgorithm.mInitialScrollP);
- return Float.compare(prevStackScrollP, mStackScrollP) != 0;
+ return Float.compare(prevScroll, mStackScrollP) != 0;
}
/**
@@ -227,10 +248,9 @@
boolean computeScroll() {
if (mScroller.computeScrollOffset()) {
float deltaP = mLayoutAlgorithm.getDeltaPForY(mFlingDownY, mScroller.getCurrY());
- float scroll = mFlingDownScrollP + deltaP;
- setStackScroll(scroll);
+ mFlingDownScrollP += setDeltaStackScroll(mFlingDownScrollP, deltaP);
if (DEBUG) {
- Log.d(TAG, "computeScroll: " + scroll);
+ Log.d(TAG, "computeScroll: " + (mFlingDownScrollP + deltaP));
}
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index ee0de1a..3cdb1fb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -200,6 +200,7 @@
// Stop the current scroll if it is still flinging
mScroller.stopScroller();
mScroller.stopBoundScrollAnimation();
+ mScroller.resetDeltaScroll();
Utilities.cancelAnimationWithoutCallbacks(mScrollFlingAnimator);
// Finish any existing task animations from the delete
@@ -223,6 +224,7 @@
mDownY = (int) ev.getY(index);
mLastY = mDownY;
mDownScrollP = mScroller.getStackScroll();
+ mScroller.resetDeltaScroll();
mVelocityTracker.addMovement(ev);
break;
}
@@ -256,20 +258,21 @@
// If we just move linearly on the screen, then that would map to 1/arclength
// of the curve, so just move the scroll proportional to that
float deltaP = layoutAlgorithm.getDeltaPForY(mDownY, y);
- float curScrollP = mDownScrollP + deltaP;
// Modulate the overscroll to prevent users from pulling the stack too far
float minScrollP = layoutAlgorithm.mMinScrollP;
float maxScrollP = layoutAlgorithm.mMaxScrollP;
+ float curScrollP = mDownScrollP + deltaP;
if (curScrollP < minScrollP || curScrollP > maxScrollP) {
float clampedScrollP = Utilities.clamp(curScrollP, minScrollP, maxScrollP);
float overscrollP = (curScrollP - clampedScrollP);
float overscrollX = Math.abs(overscrollP) / MAX_OVERSCROLL;
- curScrollP = clampedScrollP + (Math.signum(overscrollP) *
- (OVERSCROLL_INTERP.getInterpolation(overscrollX) * MAX_OVERSCROLL));
+ float interpX = OVERSCROLL_INTERP.getInterpolation(overscrollX);
+ curScrollP = clampedScrollP + Math.signum(overscrollP) *
+ (interpX * MAX_OVERSCROLL);
}
-
- mScroller.setStackScroll(curScrollP);
+ mDownScrollP += mScroller.setDeltaStackScroll(mDownScrollP,
+ curScrollP - mDownScrollP);
mStackViewScrolledEvent.updateY(y - mLastY);
EventBus.getDefault().send(mStackViewScrolledEvent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 6e585ae..6be8a4a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -39,6 +39,7 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewOutlineProvider;
+import android.widget.TextView;
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
@@ -157,6 +158,7 @@
@ViewDebug.ExportedProperty(deepExport=true, prefix="header_")
TaskViewHeader mHeaderView;
View mActionButtonView;
+ View mIncompatibleAppToastView;
TaskViewCallbacks mCb;
@ViewDebug.ExportedProperty(category="recents")
@@ -345,6 +347,9 @@
mActionButtonView.setScaleY(1f);
mActionButtonView.setAlpha(0f);
mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
+ if (mIncompatibleAppToastView != null) {
+ mIncompatibleAppToastView.setVisibility(View.INVISIBLE);
+ }
}
/**
@@ -536,6 +541,10 @@
// These values will be animated in when onStartLaunchTargetEnterAnimation() is called
setDimAlphaWithoutHeader(0);
mActionButtonView.setAlpha(0f);
+ if (mIncompatibleAppToastView != null &&
+ mIncompatibleAppToastView.getVisibility() == View.VISIBLE) {
+ mIncompatibleAppToastView.setAlpha(0f);
+ }
}
@Override
@@ -554,6 +563,15 @@
if (screenPinningEnabled) {
showActionButton(true /* fadeIn */, duration /* fadeInDuration */);
}
+
+ if (mIncompatibleAppToastView != null &&
+ mIncompatibleAppToastView.getVisibility() == View.VISIBLE) {
+ mIncompatibleAppToastView.animate()
+ .alpha(1f)
+ .setDuration(duration)
+ .setInterpolator(Interpolators.ALPHA_IN)
+ .start();
+ }
}
@Override
@@ -587,6 +605,18 @@
mTask = t;
mTask.addCallback(this);
mIsDisabledInSafeMode = !mTask.isSystemApp && ssp.isInSafeMode();
+
+ if (!t.isDockable && ssp.hasDockedTask()) {
+ if (mIncompatibleAppToastView == null) {
+ mIncompatibleAppToastView = Utilities.findViewStubById(this,
+ R.id.incompatible_app_toast_stub).inflate();
+ TextView msg = (TextView) findViewById(com.android.internal.R.id.message);
+ msg.setText(R.string.recents_incompatible_app_message);
+ }
+ mIncompatibleAppToastView.setVisibility(View.VISIBLE);
+ } else if (mIncompatibleAppToastView != null) {
+ mIncompatibleAppToastView.setVisibility(View.INVISIBLE);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 570ff8a..16d8e53 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -39,7 +39,6 @@
import android.view.ViewAnimationUtils;
import android.view.ViewDebug;
import android.view.ViewGroup;
-import android.view.ViewStub;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
@@ -141,15 +140,12 @@
// Header views
ImageView mIconView;
TextView mTitleView;
- TextView mSubTitleView;
ImageView mMoveTaskButton;
ImageView mDismissButton;
- ViewStub mAppOverlayViewStub;
FrameLayout mAppOverlayView;
ImageView mAppIconView;
ImageView mAppInfoView;
TextView mAppTitleView;
- ViewStub mFocusTimerIndicatorStub;
ProgressBar mFocusTimerIndicator;
// Header drawables
@@ -242,13 +238,10 @@
mIconView.setClickable(false);
mIconView.setOnLongClickListener(this);
mTitleView = (TextView) findViewById(R.id.title);
- mSubTitleView = (TextView) findViewById(R.id.sub_title);
mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
if (ssp.hasFreeformWorkspaceSupport()) {
mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
}
- mFocusTimerIndicatorStub = (ViewStub) findViewById(R.id.focus_timer_indicator_stub);
- mAppOverlayViewStub = (ViewStub) findViewById(R.id.app_overlay_stub);
onConfigurationChanged();
}
@@ -305,8 +298,7 @@
R.dimen.recents_task_view_header_button_padding_tablet_land,
R.dimen.recents_task_view_header_button_padding,
R.dimen.recents_task_view_header_button_padding_tablet_land);
- updateLayoutParams(mIconView, findViewById(R.id.title_container), mMoveTaskButton,
- mDismissButton);
+ updateLayoutParams(mIconView, mTitleView, mMoveTaskButton, mDismissButton);
if (mAppOverlayView != null) {
updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
}
@@ -462,13 +454,6 @@
mTitleView.setContentDescription(t.titleDescription);
mTitleView.setTextColor(t.useLightOnPrimaryColor ?
mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
- if (!t.isDockable && ssp.hasDockedTask()) {
- mSubTitleView.setVisibility(View.VISIBLE);
- mSubTitleView.setTextColor(t.useLightOnPrimaryColor ?
- mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
- } else {
- mSubTitleView.setVisibility(View.GONE);
- }
mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
mLightDismissDrawable : mDarkDismissDrawable);
mDismissButton.setContentDescription(t.dismissDescription);
@@ -491,7 +476,8 @@
if (Recents.getDebugFlags().isFastToggleRecentsEnabled()) {
if (mFocusTimerIndicator == null) {
- mFocusTimerIndicator = (ProgressBar) mFocusTimerIndicatorStub.inflate();
+ mFocusTimerIndicator = (ProgressBar) Utilities.findViewStubById(this,
+ R.id.focus_timer_indicator_stub).inflate();
}
mFocusTimerIndicator.getProgressDrawable()
.setColorFilter(
@@ -637,7 +623,8 @@
// Inflate the overlay if necessary
if (mAppOverlayView == null) {
- mAppOverlayView = (FrameLayout) mAppOverlayViewStub.inflate();
+ mAppOverlayView = (FrameLayout) Utilities.findViewStubById(this,
+ R.id.app_overlay_stub).inflate();
mAppOverlayView.setBackground(mOverlayBackground);
mAppIconView = (ImageView) mAppOverlayView.findViewById(R.id.app_icon);
mAppIconView.setOnClickListener(this);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index e5ac0d3..62fd585 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -143,43 +143,28 @@
int viewWidth = mTaskViewRect.width();
int viewHeight = mTaskViewRect.height();
- if (mBitmapShader != null) {
-
- // We are drawing the thumbnail in the same orientation, so just fit the width
- int thumbnailWidth = (int) (mThumbnailRect.width() * mThumbnailScale);
- int thumbnailHeight = (int) (mThumbnailRect.height() * mThumbnailScale);
-
- if (thumbnailWidth >= viewWidth && thumbnailHeight >= viewHeight) {
- // Thumbnail fills the full task view bounds, so just draw it
- canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
- mDrawPaint);
- } else {
- // Thumbnail does not fill the full task view bounds, so just draw it and fill the
- // empty areas with the background color
- int count = canvas.save();
-
- // Since we only want the top corners to be rounded, draw slightly beyond the
- // thumbnail height, but clip to the thumbnail height
- canvas.clipRect(0, 0, thumbnailWidth, thumbnailHeight, Region.Op.REPLACE);
- canvas.drawRoundRect(0, 0,
- thumbnailWidth + (thumbnailWidth < viewWidth ? mCornerRadius : 0),
- thumbnailHeight + (thumbnailHeight < viewHeight ? mCornerRadius : 0),
- mCornerRadius, mCornerRadius, mDrawPaint);
-
- // In the remaining space, draw the background color
- if (thumbnailWidth < viewWidth) {
- canvas.clipRect(thumbnailWidth, 0, viewWidth, viewHeight, Region.Op.REPLACE);
- canvas.drawRoundRect(Math.max(0, thumbnailWidth - mCornerRadius), 0,
- viewWidth, viewHeight, mCornerRadius, mCornerRadius, mBgFillPaint);
- }
- if (thumbnailWidth > 0 && thumbnailHeight < viewHeight) {
- canvas.clipRect(0, thumbnailHeight, viewWidth, viewHeight, Region.Op.REPLACE);
- canvas.drawRoundRect(0, Math.max(0, thumbnailHeight - mCornerRadius),
- viewWidth, viewHeight, mCornerRadius, mCornerRadius, mBgFillPaint);
- }
-
- canvas.restoreToCount(count);
+ int thumbnailWidth = Math.min(viewWidth,
+ (int) (mThumbnailRect.width() * mThumbnailScale));
+ int thumbnailHeight = Math.min(viewHeight,
+ (int) (mThumbnailRect.height() * mThumbnailScale));
+ if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
+ // Draw the background, there will be some small overdraw with the thumbnail
+ if (thumbnailWidth < viewWidth) {
+ // Portrait thumbnail on a landscape task view
+ canvas.drawRoundRect(Math.max(0, thumbnailWidth - mCornerRadius), 0,
+ viewWidth, viewHeight,
+ mCornerRadius, mCornerRadius, mBgFillPaint);
}
+ if (thumbnailHeight < viewHeight) {
+ // Landscape thumbnail on a portrait task view
+ canvas.drawRoundRect(0, Math.max(0, thumbnailHeight - mCornerRadius),
+ viewWidth, viewHeight,
+ mCornerRadius, mCornerRadius, mBgFillPaint);
+ }
+
+ // Draw the thumbnail
+ canvas.drawRoundRect(0, 0, thumbnailWidth, thumbnailHeight,
+ mCornerRadius, mCornerRadius, mDrawPaint);
} else {
canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
mBgFillPaint);
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 69dcabe..0aeb7b4 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -102,8 +102,8 @@
int dockMode = (shortcutCode == SC_DOCK_LEFT)
? ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
: ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
- recents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE, dockMode, null);
- MetricsLogger.action(mContext, MetricsEvent.WINDOW_DOCK_SHORTCUTS);
+ recents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE, dockMode, null,
+ MetricsEvent.WINDOW_DOCK_SHORTCUTS);
} else {
// If there is already a docked window, we respond by resizing the docking pane.
DividerView dividerView = getComponent(Divider.class).getView();
@@ -115,7 +115,8 @@
DividerSnapAlgorithm.SnapTarget target = snapAlgorithm.cycleNonDismissTarget(
currentTarget, increment);
dividerView.startDragging(true /* animate */, false /* touching */);
- dividerView.stopDragging(target.position, 0f, true /* avoidDismissStart */);
+ dividerView.stopDragging(target.position, 0f, true /* avoidDismissStart */,
+ true /* logMetrics */);
}
} catch (RemoteException e) {
Log.e(TAG, "handleDockKey() failed.");
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index d294c80..2bf0b40 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -146,5 +146,10 @@
throws RemoteException {
updateMinimizedDockedStack(minimized, animDuration);
}
+
+ @Override
+ public void onDockSideChanged(final int newDockSide) throws RemoteException {
+ mView.post(() -> mView.notifyDockSideChanged(newDockSide));
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 3005535..a6c75e8 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -31,12 +31,13 @@
import android.graphics.Region.Op;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
+import android.os.Vibrator;
import android.util.AttributeSet;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.GestureDetector;
-import android.view.GestureDetector.OnDoubleTapListener;
import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.VelocityTracker;
@@ -45,7 +46,6 @@
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver.InternalInsetsInfo;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -54,11 +54,14 @@
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
import com.android.internal.policy.DockedDividerUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.recents.Constants.Metrics;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
@@ -80,7 +83,13 @@
static final long TOUCH_ANIMATION_DURATION = 150;
static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
- private static final String TAG = "DividerView";
+ private static final int LOG_VALUE_RESIZE_50_50 = 0;
+ private static final int LOG_VALUE_RESIZE_DOCKED_SMALLER = 1;
+ private static final int LOG_VALUE_RESIZE_DOCKED_LARGER = 2;
+
+ private static final int LOG_VALUE_UNDOCK_MAX_DOCKED = 0;
+ private static final int LOG_VALUE_UNDOCK_MAX_OTHER = 1;
+
private static final int TASK_POSITION_SAME = Integer.MAX_VALUE;
private static final boolean SWAPPING_ENABLED = false;
@@ -88,7 +97,7 @@
/**
* How much the background gets scaled when we are in the minimized dock state.
*/
- private static final float MINIMIZE_DOCK_SCALE = 0.375f;
+ private static final float MINIMIZE_DOCK_SCALE = 0f;
private static final PathInterpolator SLOWDOWN_INTERPOLATOR =
new PathInterpolator(0.5f, 1f, 0.5f, 1f);
@@ -97,6 +106,7 @@
private DividerHandleView mHandle;
private View mBackground;
+ private MinimizedDockShadow mMinimizedShadow;
private int mStartX;
private int mStartY;
private int mStartPosition;
@@ -133,6 +143,8 @@
private boolean mGrowRecents;
private ValueAnimator mCurrentAnimator;
private boolean mEntranceAnimationRunning;
+ private boolean mExitAnimationRunning;
+ private int mExitStartPosition;
private GestureDetector mGestureDetector;
private boolean mDockedStackMinimized;
@@ -201,6 +213,7 @@
super.onFinishInflate();
mHandle = (DividerHandleView) findViewById(R.id.docked_divider_handle);
mBackground = findViewById(R.id.docked_divider_background);
+ mMinimizedShadow = (MinimizedDockShadow) findViewById(R.id.minimized_dock_shadow);
mHandle.setOnTouchListener(this);
mDividerWindowWidth = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_thickness);
@@ -267,6 +280,18 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
+ int minimizeLeft = 0;
+ int minimizeTop = 0;
+ if (mDockSide == WindowManager.DOCKED_TOP) {
+ minimizeTop = mBackground.getTop();
+ } else if (mDockSide == WindowManager.DOCKED_LEFT) {
+ minimizeLeft = mBackground.getLeft();
+ } else if (mDockSide == WindowManager.DOCKED_RIGHT) {
+ minimizeLeft = mBackground.getRight() - mMinimizedShadow.getWidth();
+ }
+ mMinimizedShadow.layout(minimizeLeft, minimizeTop,
+ minimizeLeft + mMinimizedShadow.getMeasuredWidth(),
+ minimizeTop + mMinimizedShadow.getMeasuredHeight());
if (changed) {
mWindowManagerProxy.setTouchRegion(new Rect(mHandle.getLeft(), mHandle.getTop(),
mHandle.getRight(), mHandle.getBottom()));
@@ -297,9 +322,10 @@
return mDockSide != WindowManager.DOCKED_INVALID;
}
- public void stopDragging(int position, float velocity, boolean avoidDismissStart) {
+ public void stopDragging(int position, float velocity, boolean avoidDismissStart,
+ boolean logMetrics) {
mHandle.setTouching(false, true /* animate */);
- fling(position, velocity, avoidDismissStart);
+ fling(position, velocity, avoidDismissStart, logMetrics);
mWindowManager.setSlippery(true);
releaseBackground();
}
@@ -325,6 +351,7 @@
private void updateDockSide() {
mDockSide = mWindowManagerProxy.getDockSide();
+ mMinimizedShadow.setDockSide(mDockSide);
}
private void initializeSnapAlgorithm() {
@@ -396,22 +423,54 @@
mVelocityTracker.computeCurrentVelocity(1000);
int position = calculatePosition(x, y);
stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity()
- : mVelocityTracker.getXVelocity(), false /* avoidDismissStart */);
+ : mVelocityTracker.getXVelocity(), false /* avoidDismissStart */,
+ true /* log */);
mMoving = false;
break;
}
return true;
}
+ private void logResizeEvent(SnapTarget snapTarget) {
+ if (snapTarget == mSnapAlgorithm.getDismissStartTarget()) {
+ MetricsLogger.action(
+ mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideTopLeft(mDockSide)
+ ? LOG_VALUE_UNDOCK_MAX_OTHER
+ : LOG_VALUE_UNDOCK_MAX_DOCKED);
+ } else if (snapTarget == mSnapAlgorithm.getDismissEndTarget()) {
+ MetricsLogger.action(
+ mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideBottomRight(mDockSide)
+ ? LOG_VALUE_UNDOCK_MAX_OTHER
+ : LOG_VALUE_UNDOCK_MAX_DOCKED);
+ } else if (snapTarget == mSnapAlgorithm.getMiddleTarget()) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
+ LOG_VALUE_RESIZE_50_50);
+ } else if (snapTarget == mSnapAlgorithm.getFirstSplitTarget()) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
+ dockSideTopLeft(mDockSide)
+ ? LOG_VALUE_RESIZE_DOCKED_SMALLER
+ : LOG_VALUE_RESIZE_DOCKED_LARGER);
+ } else if (snapTarget == mSnapAlgorithm.getLastSplitTarget()) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
+ dockSideTopLeft(mDockSide)
+ ? LOG_VALUE_RESIZE_DOCKED_LARGER
+ : LOG_VALUE_RESIZE_DOCKED_SMALLER);
+ }
+ }
+
private void convertToScreenCoordinates(MotionEvent event) {
event.setLocation(event.getRawX(), event.getRawY());
}
- private void fling(int position, float velocity, boolean avoidDismissStart) {
+ private void fling(int position, float velocity, boolean avoidDismissStart,
+ boolean logMetrics) {
SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position, velocity);
if (avoidDismissStart && snapTarget == mSnapAlgorithm.getDismissStartTarget()) {
snapTarget = mSnapAlgorithm.getFirstSplitTarget();
}
+ if (logMetrics) {
+ logResizeEvent(snapTarget);
+ }
ValueAnimator anim = getFlingAnimator(position, snapTarget);
mFlingAnimationUtils.apply(anim, position, snapTarget.position, velocity);
anim.start();
@@ -445,6 +504,7 @@
mDockSide = WindowManager.DOCKED_INVALID;
mCurrentAnimator = null;
mEntranceAnimationRunning = false;
+ mExitAnimationRunning = false;
EventBus.getDefault().send(new StoppedDragingEvent());
}
});
@@ -538,6 +598,7 @@
: mBackground.getWidth());
mBackground.setScaleX(MINIMIZE_DOCK_SCALE);
}
+ mMinimizedShadow.setAlpha(minimized ? 1f : 0f);
mDockedStackMinimized = minimized;
}
@@ -563,6 +624,11 @@
if (!minimized) {
mBackground.animate().withEndAction(mResetBackgroundRunnable);
}
+ mMinimizedShadow.animate()
+ .alpha(minimized ? 1f : 0f)
+ .setInterpolator(Interpolators.ALPHA_IN)
+ .setDuration(animDuration)
+ .start();
mBackground.animate()
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.setDuration(animDuration)
@@ -575,6 +641,7 @@
mBackground.setPivotY(mBackground.getHeight() / 2);
mBackground.setScaleX(1f);
mBackground.setScaleY(1f);
+ mMinimizedShadow.setAlpha(0f);
}
@Override
@@ -583,6 +650,13 @@
updateDisplayInfo();
}
+
+ public void notifyDockSideChanged(int newDockSide) {
+ mDockSide = newDockSide;
+ mMinimizedShadow.setDockSide(mDockSide);
+ requestLayout();
+ }
+
private void updateDisplayInfo() {
final DisplayManager displayManager =
(DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
@@ -654,6 +728,13 @@
mOtherTaskRect);
mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, null,
mOtherTaskRect, null);
+ } else if (mExitAnimationRunning && taskPosition != TASK_POSITION_SAME) {
+ calculateBoundsForPosition(taskPosition,
+ mDockSide, mDockedTaskRect);
+ calculateBoundsForPosition(mExitStartPosition,
+ DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect);
+ mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, null,
+ mOtherTaskRect, null);
} else if (taskPosition != TASK_POSITION_SAME) {
calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
mOtherRect);
@@ -866,6 +947,9 @@
mEntranceAnimationRunning = true;
resizeStack(position, mSnapAlgorithm.getMiddleTarget().position,
mSnapAlgorithm.getMiddleTarget());
+
+ // Vibrate after docking
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
public final void onBusEvent(RecentsDrawnEvent drawnEvent) {
@@ -892,8 +976,13 @@
: mSnapAlgorithm.getDismissStartTarget();
// Don't start immediately - give a little bit time to settle the drag resize change.
- stopDragging(getCurrentPosition(), target, 336 /* duration */, 100 /* startDelay */,
+ mExitAnimationRunning = true;
+ mExitStartPosition = getCurrentPosition();
+ stopDragging(mExitStartPosition, target, 336 /* duration */, 100 /* startDelay */,
Interpolators.TOUCH_RESPONSE);
+
+ // Vibrate after undocking
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
index d4922c3..177296b 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
@@ -48,6 +48,7 @@
setContentView(R.layout.forced_resizable_activity);
TextView tv = (TextView) findViewById(com.android.internal.R.id.message);
tv.setText(R.string.dock_forced_resizable);
+ getWindow().setTitle(getString(R.string.dock_forced_resizable));
getWindow().getDecorView().setOnTouchListener(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/MinimizedDockShadow.java b/packages/SystemUI/src/com/android/systemui/stackdivider/MinimizedDockShadow.java
new file mode 100644
index 0000000..ecff54f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/MinimizedDockShadow.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.stackdivider;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+
+/**
+ * Shadow for the minimized dock state on homescreen.
+ */
+public class MinimizedDockShadow extends View {
+
+ private final Paint mShadowPaint = new Paint();
+
+ private int mDockSide = WindowManager.DOCKED_INVALID;
+
+ public MinimizedDockShadow(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setDockSide(int dockSide) {
+ if (dockSide != mDockSide) {
+ mDockSide = dockSide;
+ updatePaint(getLeft(), getTop(), getRight(), getBottom());
+ invalidate();
+ }
+ }
+
+ private void updatePaint(int left, int top, int right, int bottom) {
+ int startColor = mContext.getResources().getColor(
+ R.color.minimize_dock_shadow_start, null);
+ int endColor = mContext.getResources().getColor(
+ R.color.minimize_dock_shadow_end, null);
+ final int middleColor = Color.argb(
+ (Color.alpha(startColor) + Color.alpha(endColor)) / 2, 0, 0, 0);
+ final int quarter = Color.argb(
+ (int) (Color.alpha(startColor) * 0.25f + Color.alpha(endColor) * 0.75f),
+ 0, 0, 0);
+ if (mDockSide == WindowManager.DOCKED_TOP) {
+ mShadowPaint.setShader(new LinearGradient(
+ 0, 0, 0, bottom - top,
+ new int[] { startColor, middleColor, quarter, endColor },
+ new float[] { 0f, 0.35f, 0.6f, 1f }, Shader.TileMode.CLAMP));
+ } else if (mDockSide == WindowManager.DOCKED_LEFT) {
+ mShadowPaint.setShader(new LinearGradient(
+ 0, 0, right - left, 0,
+ new int[] { startColor, middleColor, quarter, endColor },
+ new float[] { 0f, 0.35f, 0.6f, 1f }, Shader.TileMode.CLAMP));
+ } else if (mDockSide == WindowManager.DOCKED_RIGHT) {
+ mShadowPaint.setShader(new LinearGradient(
+ right - left, 0, 0, 0,
+ new int[] { startColor, middleColor, quarter, endColor },
+ new float[] { 0f, 0.35f, 0.6f, 1f }, Shader.TileMode.CLAMP));
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (changed) {
+ updatePaint(left, top, right, bottom);
+ invalidate();
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.drawRect(0, 0, getWidth(), getHeight(), mShadowPaint);
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 1b2393a..11a7048 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -63,6 +63,8 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -262,11 +264,24 @@
protected AssistManager mAssistManager;
+ protected boolean mVrMode;
+
@Override // NotificationData.Environment
public boolean isDeviceProvisioned() {
return mDeviceProvisioned;
}
+ private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
+ @Override
+ public void onVrStateChanged(boolean enabled) {
+ mVrMode = enabled;
+ }
+ };
+
+ public boolean isDeviceInVrMode() {
+ return mVrMode;
+ }
+
protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
@@ -776,6 +791,14 @@
mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
null, null);
updateCurrentProfilesCache();
+
+ IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService("vrmanager"));
+ try {
+ vrManager.registerListener(mVrStateCallbacks);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+ }
+
}
protected void notifyUserAboutHiddenNotifications() {
@@ -1207,7 +1230,7 @@
@Override
public void toggleSplitScreen() {
- toggleSplitScreenMode();
+ toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */);
}
@Override
@@ -1283,9 +1306,12 @@
/**
* Toggle docking the app window
*
- * @return {@code true} if the app window is docked after the toggle, {@code false} otherwise.
+ * @param metricsDockAction the action to log when docking is successful, or -1 to not log
+ * anything on successful docking
+ * @param metricsUndockAction the action to log when undocking, or -1 to not log anything when
+ * undocking
*/
- protected abstract boolean toggleSplitScreenMode();
+ protected abstract void toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction);
/** Proxy for RecentsComponent */
@@ -2353,6 +2379,10 @@
}
protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
+ if (isDeviceInVrMode()) {
+ return false;
+ }
+
if (mNotificationData.shouldFilterOut(sbn)) {
if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index fff1491..c2521b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -58,6 +58,8 @@
import com.android.systemui.recents.Recents;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
@@ -76,11 +78,32 @@
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Context mContext;
private final IPackageManager mPackageManager;
- private final OnClickListener dialogCloseListener = new DialogInterface.OnClickListener() {
+ private final OnClickListener mDialogCloseListener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dismissKeyboardShortcutsDialog();
}
};
+ private final Comparator<KeyboardShortcutInfo> mApplicationItemsComparator =
+ new Comparator<KeyboardShortcutInfo>() {
+ @Override
+ public int compare(KeyboardShortcutInfo ksh1, KeyboardShortcutInfo ksh2) {
+ boolean ksh1ShouldBeLast = ksh1.getLabel() == null
+ || ksh1.getLabel().toString().isEmpty();
+ boolean ksh2ShouldBeLast = ksh2.getLabel() == null
+ || ksh2.getLabel().toString().isEmpty();
+ if (ksh1ShouldBeLast && ksh2ShouldBeLast) {
+ return 0;
+ }
+ if (ksh1ShouldBeLast) {
+ return 1;
+ }
+ if (ksh2ShouldBeLast) {
+ return -1;
+ }
+ return (ksh1.getLabel().toString()).compareToIgnoreCase(
+ ksh2.getLabel().toString());
+ }
+ };
private Dialog mKeyboardShortcutsDialog;
private KeyCharacterMap mKeyCharacterMap;
@@ -255,10 +278,7 @@
}
public void toggleKeyboardShortcuts(int deviceId) {
- InputDevice inputDevice = InputManager.getInstance().getInputDevice(deviceId);
- if (inputDevice != null) {
- mKeyCharacterMap = inputDevice.getKeyCharacterMap();
- }
+ retrieveKeyCharacterMap(deviceId);
if (mKeyboardShortcutsDialog == null) {
Recents.getSystemServices().requestKeyboardShortcuts(mContext,
new KeyboardShortcutsReceiver() {
@@ -278,6 +298,35 @@
}
}
+ /**
+ * Retrieves a {@link KeyCharacterMap} and assigns it to mKeyCharacterMap. If the given id is an
+ * existing device, that device's map is used. Otherwise, it checks first all available devices
+ * and if there is a full keyboard it uses that map, otherwise falls back to the Virtual
+ * Keyboard with its default map.
+ */
+ private void retrieveKeyCharacterMap(int deviceId) {
+ final InputManager inputManager = InputManager.getInstance();
+ if (deviceId != -1) {
+ final InputDevice inputDevice = inputManager.getInputDevice(deviceId);
+ if (inputDevice != null) {
+ mKeyCharacterMap = inputDevice.getKeyCharacterMap();
+ return;
+ }
+ }
+ final int[] deviceIds = inputManager.getInputDeviceIds();
+ for (int i = 0; i < deviceIds.length; ++i) {
+ final InputDevice inputDevice = inputManager.getInputDevice(deviceIds[i]);
+ // -1 is the Virtual Keyboard, with the default key map. Use that one only as last
+ // resort.
+ if (inputDevice.getId() != -1 && inputDevice.isFullKeyboard()) {
+ mKeyCharacterMap = inputDevice.getKeyCharacterMap();
+ return;
+ }
+ }
+ final InputDevice inputDevice = inputManager.getInputDevice(-1);
+ mKeyCharacterMap = inputDevice.getKeyCharacterMap();
+ }
+
public void dismissKeyboardShortcutsDialog() {
if (mKeyboardShortcutsDialog != null) {
mKeyboardShortcutsDialog.dismiss();
@@ -320,9 +369,7 @@
private KeyboardShortcutGroup getDefaultApplicationShortcuts() {
final int userId = mContext.getUserId();
- final KeyboardShortcutGroup applicationGroup = new KeyboardShortcutGroup(
- mContext.getString(R.string.keyboard_shortcut_group_applications),
- true);
+ List<KeyboardShortcutInfo> keyboardShortcutInfoAppItems = new ArrayList<>();
// Assist.
final AssistUtils assistUtils = new AssistUtils(mContext);
@@ -340,7 +387,7 @@
assistPackageInfo.applicationInfo.packageName,
assistPackageInfo.applicationInfo.icon);
- applicationGroup.addItem(new KeyboardShortcutInfo(
+ keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
mContext.getString(R.string.keyboard_shortcut_group_applications_assist),
assistIcon,
KeyEvent.KEYCODE_UNKNOWN,
@@ -350,7 +397,7 @@
// Browser.
final Icon browserIcon = getIconForIntentCategory(Intent.CATEGORY_APP_BROWSER, userId);
if (browserIcon != null) {
- applicationGroup.addItem(new KeyboardShortcutInfo(
+ keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
mContext.getString(R.string.keyboard_shortcut_group_applications_browser),
browserIcon,
KeyEvent.KEYCODE_B,
@@ -361,7 +408,7 @@
// Contacts.
final Icon contactsIcon = getIconForIntentCategory(Intent.CATEGORY_APP_CONTACTS, userId);
if (contactsIcon != null) {
- applicationGroup.addItem(new KeyboardShortcutInfo(
+ keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
mContext.getString(R.string.keyboard_shortcut_group_applications_contacts),
contactsIcon,
KeyEvent.KEYCODE_C,
@@ -371,7 +418,7 @@
// Email.
final Icon emailIcon = getIconForIntentCategory(Intent.CATEGORY_APP_EMAIL, userId);
if (emailIcon != null) {
- applicationGroup.addItem(new KeyboardShortcutInfo(
+ keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
mContext.getString(R.string.keyboard_shortcut_group_applications_email),
emailIcon,
KeyEvent.KEYCODE_E,
@@ -381,7 +428,7 @@
// Messaging.
final Icon messagingIcon = getIconForIntentCategory(Intent.CATEGORY_APP_MESSAGING, userId);
if (messagingIcon != null) {
- applicationGroup.addItem(new KeyboardShortcutInfo(
+ keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
mContext.getString(R.string.keyboard_shortcut_group_applications_im),
messagingIcon,
KeyEvent.KEYCODE_T,
@@ -391,7 +438,7 @@
// Music.
final Icon musicIcon = getIconForIntentCategory(Intent.CATEGORY_APP_MUSIC, userId);
if (musicIcon != null) {
- applicationGroup.addItem(new KeyboardShortcutInfo(
+ keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
mContext.getString(R.string.keyboard_shortcut_group_applications_music),
musicIcon,
KeyEvent.KEYCODE_P,
@@ -401,14 +448,24 @@
// Calendar.
final Icon calendarIcon = getIconForIntentCategory(Intent.CATEGORY_APP_CALENDAR, userId);
if (calendarIcon != null) {
- applicationGroup.addItem(new KeyboardShortcutInfo(
+ keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
mContext.getString(R.string.keyboard_shortcut_group_applications_calendar),
calendarIcon,
KeyEvent.KEYCODE_L,
KeyEvent.META_META_ON));
}
- return applicationGroup.getItems().size() == 0 ? null : applicationGroup;
+ final int itemsSize = keyboardShortcutInfoAppItems.size();
+ if (itemsSize == 0) {
+ return null;
+ }
+
+ // Sorts by label, case insensitive with nulls and/or empty labels last.
+ Collections.sort(keyboardShortcutInfoAppItems, mApplicationItemsComparator);
+ return new KeyboardShortcutGroup(
+ mContext.getString(R.string.keyboard_shortcut_group_applications),
+ keyboardShortcutInfoAppItems,
+ true);
}
private Icon getIconForIntentCategory(String intentCategory, int userId) {
@@ -459,7 +516,7 @@
populateKeyboardShortcuts((LinearLayout) keyboardShortcutsView.findViewById(
R.id.keyboard_shortcuts_container), keyboardShortcutGroups);
dialogBuilder.setView(keyboardShortcutsView);
- dialogBuilder.setPositiveButton(R.string.quick_settings_done, dialogCloseListener);
+ dialogBuilder.setPositiveButton(R.string.quick_settings_done, mDialogCloseListener);
mKeyboardShortcutsDialog = dialogBuilder.create();
mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true);
Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow();
@@ -471,8 +528,15 @@
List<KeyboardShortcutGroup> keyboardShortcutGroups) {
LayoutInflater inflater = LayoutInflater.from(mContext);
final int keyboardShortcutGroupsSize = keyboardShortcutGroups.size();
+ TextView shortcutsKeyView = (TextView) inflater.inflate(
+ R.layout.keyboard_shortcuts_key_view, null, false);
+ shortcutsKeyView.measure(
+ View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ final int shortcutKeyTextItemMinWidth = shortcutsKeyView.getMeasuredHeight();
// Needed to be able to scale the image items to the same height as the text items.
- final int shortcutTextItemHeight = getShortcutTextItemHeight(inflater);
+ final int shortcutKeyIconItemHeightWidth = shortcutsKeyView.getMeasuredHeight()
+ - shortcutsKeyView.getPaddingTop()
+ - shortcutsKeyView.getPaddingBottom();
for (int i = 0; i < keyboardShortcutGroupsSize; i++) {
KeyboardShortcutGroup group = keyboardShortcutGroups.get(i);
TextView categoryTitle = (TextView) inflater.inflate(
@@ -488,12 +552,6 @@
final int itemsSize = group.getItems().size();
for (int j = 0; j < itemsSize; j++) {
KeyboardShortcutInfo info = group.getItems().get(j);
- if (info.getKeycode() != KeyEvent.KEYCODE_UNKNOWN
- && !KeyCharacterMap.deviceHasKey(info.getKeycode())) {
- // The user can't achieve this shortcut, so skipping.
- Log.w(TAG, "Keyboard Shortcut contains key not on device, skipping.");
- continue;
- }
List<StringOrDrawable> shortcutKeys = getHumanReadableShortcutKeys(info);
if (shortcutKeys == null) {
// Ignore shortcuts we can't display keys for.
@@ -529,8 +587,8 @@
ImageView shortcutKeyIconView = (ImageView) inflater.inflate(
R.layout.keyboard_shortcuts_key_icon_view, shortcutItemsContainer,
false);
- Bitmap bitmap = Bitmap.createBitmap(shortcutTextItemHeight,
- shortcutTextItemHeight, Bitmap.Config.ARGB_8888);
+ Bitmap bitmap = Bitmap.createBitmap(shortcutKeyIconItemHeightWidth,
+ shortcutKeyIconItemHeightWidth, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
shortcutRepresentation.drawable.setBounds(0, 0, canvas.getWidth(),
canvas.getHeight());
@@ -541,6 +599,7 @@
TextView shortcutKeyTextView = (TextView) inflater.inflate(
R.layout.keyboard_shortcuts_key_view, shortcutItemsContainer,
false);
+ shortcutKeyTextView.setMinimumWidth(shortcutKeyTextItemMinWidth);
shortcutKeyTextView.setText(shortcutRepresentation.string);
shortcutItemsContainer.addView(shortcutKeyTextView);
}
@@ -557,16 +616,6 @@
}
}
- private int getShortcutTextItemHeight(LayoutInflater inflater) {
- TextView shortcutKeyTextView = (TextView) inflater.inflate(
- R.layout.keyboard_shortcuts_key_view, null, false);
- shortcutKeyTextView.measure(
- View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- return shortcutKeyTextView.getMeasuredHeight()
- - shortcutKeyTextView.getPaddingTop()
- - shortcutKeyTextView.getPaddingBottom();
- }
-
private List<StringOrDrawable> getHumanReadableShortcutKeys(KeyboardShortcutInfo info) {
List<StringOrDrawable> shortcutKeys = getHumanReadableModifiers(info);
if (shortcutKeys == null) {
@@ -585,9 +634,7 @@
if (info.getKeycode() == KeyEvent.KEYCODE_UNKNOWN) {
return shortcutKeys;
}
- // TODO: Have a generic map for when we don't have the device's.
- char displayLabel = mKeyCharacterMap == null
- ? 0 : mKeyCharacterMap.getDisplayLabel(info.getKeycode());
+ char displayLabel = mKeyCharacterMap.getDisplayLabel(info.getKeycode());
if (displayLabel != 0) {
displayLabelString = String.valueOf(displayLabel);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
new file mode 100644
index 0000000..5d22faf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Receiver for the Keyboard Shortcuts Helper.
+ */
+public class KeyboardShortcutsReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
+ final KeyboardShortcuts keyboardShortcuts = new KeyboardShortcuts(context);
+ keyboardShortcuts.toggleKeyboardShortcuts(-1 /* deviceId unknown */);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 5b00523..491ffde 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -348,6 +348,12 @@
setVisible(isShown());
}
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
+ }
+
private void setVisible(final boolean isVisible) {
if (isVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
index a5ebbba..9ed5022 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
@@ -232,8 +232,9 @@
return;
}
final boolean isRtl = mParent.isLayoutRtl();
- final float left = isRtl ? -(mParent.getWidth() - mHorizSpaceForGear) : 0;
- final float right = isRtl ? 0 : (mParent.getWidth() - mHorizSpaceForGear);
+ // TODO No need to cast to float here once b/28050538 is fixed.
+ final float left = (float) (isRtl ? -(mParent.getWidth() - mHorizSpaceForGear) : 0);
+ final float right = (float) (isRtl ? 0 : (mParent.getWidth() - mHorizSpaceForGear));
setTranslationX(onLeft ? left : right);
mOnLeft = onLeft;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
index 03b51c6..244536d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
@@ -240,6 +240,11 @@
}
@Override
+ public void dispatchDemoCommand(String command, Bundle args) {
+ // TODO: Car demo mode.
+ }
+
+ @Override
public boolean isPowerSave() {
// Power save is not valid for the car, so always return false.
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index bb03454..583a63e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -214,15 +214,14 @@
< mContext.getResources().getDisplayMetrics().widthPixels / 2) {
createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
}
- boolean docked = mRecentsComponent.dockTopTask(dragMode, createMode, initialBounds);
+ boolean docked = mRecentsComponent.dockTopTask(dragMode, createMode, initialBounds,
+ MetricsEvent.ACTION_WINDOW_DOCK_SWIPE);
if (docked) {
mDragMode = dragMode;
if (mDragMode == DRAG_MODE_DIVIDER) {
mDivider.getView().startDragging(false /* animate */, true /* touching*/);
}
mDockWindowTouchSlopExceeded = true;
- MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_SWIPE);
-
return true;
}
}
@@ -250,7 +249,7 @@
mIsVertical
? mVelocityTracker.getXVelocity()
: mVelocityTracker.getYVelocity(),
- true /* avoidDismissStart */);
+ true /* avoidDismissStart */, false /* logMetrics */);
} else if (mDragMode == DRAG_MODE_RECENTS) {
mRecentsComponent.onDraggingInRecentsEnded(mVelocityTracker.getYVelocity());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 6859348..581b611 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -526,6 +526,10 @@
public void onDockedStackMinimizedChanged(boolean minimized, long animDuration)
throws RemoteException {
}
+
+ @Override
+ public void onDockSideChanged(int newDockSide) throws RemoteException {
+ }
});
} catch (RemoteException e) {
Log.e(TAG, "Failed registering docked stack exists listener", e);
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 e52a401..5423f9d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1177,31 +1177,26 @@
return false;
}
- boolean initiallyDocked = WindowManagerProxy.getInstance().getDockSide()
- == WindowManager.DOCKED_INVALID;
- boolean dockedAtEnd = toggleSplitScreenMode();
- if (dockedAtEnd != initiallyDocked) {
- int logAction = dockedAtEnd ? MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS
- : MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS;
- MetricsLogger.action(mContext, logAction);
- return true;
- }
- return false;
+ toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
+ MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
+ return true;
}
};
@Override
- protected boolean toggleSplitScreenMode() {
+ protected void toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) {
if (mRecents == null) {
- return false;
+ return;
}
int dockSide = WindowManagerProxy.getInstance().getDockSide();
if (dockSide == WindowManager.DOCKED_INVALID) {
- return mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
- ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null);
+ mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
+ ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null, metricsDockAction);
} else {
EventBus.getDefault().send(new UndockingTaskEvent());
- return false;
+ if (metricsUndockAction != -1) {
+ MetricsLogger.action(mContext, metricsUndockAction);
+ }
}
}
@@ -1374,6 +1369,10 @@
}
private boolean shouldSuppressFullScreenIntent(String key) {
+ if (isDeviceInVrMode()) {
+ return true;
+ }
+
if (mPowerManager.isInteractive()) {
return mNotificationData.shouldSuppressScreenOn(key);
} else {
@@ -3470,7 +3469,7 @@
@Override
public void run() {
mLeaveOpenOnKeyguardHide = true;
- executeRunnableDismissingKeyguard(runnable, null, false, true);
+ executeRunnableDismissingKeyguard(runnable, null, false, false);
}
});
}
@@ -3591,11 +3590,10 @@
dispatchDemoCommandToView(command, args, R.id.clock);
}
if (modeChange || command.equals(COMMAND_BATTERY)) {
- dispatchDemoCommandToView(command, args, R.id.battery);
+ mBatteryController.dispatchDemoCommand(command, args);
}
if (modeChange || command.equals(COMMAND_STATUS)) {
mIconController.dispatchDemoCommand(command, args);
-
}
if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) {
mNetworkController.dispatchDemoCommand(command, args);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index ea9a5a5..2b03dfb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -125,6 +125,7 @@
// RenderThread is doing more harm than good when touching the header (to expand quick
// settings), so disable it for this view
((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
+ ((RippleDrawable) mExpandIndicator.getBackground()).setForceSoftware(true);
updateResources();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index ea64fd8..559436b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -16,10 +16,12 @@
package com.android.systemui.statusbar.policy;
+import com.android.systemui.DemoMode;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
-public interface BatteryController {
+public interface BatteryController extends DemoMode {
/**
* Prints the current state of the {@link BatteryController} to the given {@link PrintWriter}.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 24207f3..5d734c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -21,9 +21,11 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
+import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.util.Log;
+import com.android.systemui.DemoMode;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -43,6 +45,7 @@
private final ArrayList<BatteryController.BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
private final PowerManager mPowerManager;
private final Handler mHandler;
+ private final Context mContext;
protected int mLevel;
protected boolean mPluggedIn;
@@ -52,17 +55,21 @@
private boolean mTestmode = false;
public BatteryControllerImpl(Context context) {
+ mContext = context;
mHandler = new Handler();
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ registerReceiver();
+ updatePowerSave();
+ }
+
+ private void registerReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
filter.addAction(ACTION_LEVEL_TEST);
- context.registerReceiver(this, filter);
-
- updatePowerSave();
+ mContext.registerReceiver(this, filter);
}
@Override
@@ -176,4 +183,28 @@
mChangeCallbacks.get(i).onPowerSaveChanged(mPowerSave);
}
}
+
+ private boolean mDemoMode;
+
+ @Override
+ public void dispatchDemoCommand(String command, Bundle args) {
+ if (!mDemoMode && command.equals(COMMAND_ENTER)) {
+ mDemoMode = true;
+ mContext.unregisterReceiver(this);
+ } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
+ mDemoMode = false;
+ registerReceiver();
+ updatePowerSave();
+ } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {
+ String level = args.getString("level");
+ String plugged = args.getString("plugged");
+ if (level != null) {
+ mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100);
+ }
+ if (plugged != null) {
+ mPluggedIn = Boolean.parseBoolean(plugged);
+ }
+ fireBatteryLevelChanged();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
index 6dd196b..c4c64e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
@@ -33,24 +33,29 @@
}
private void handleRestrictBackgroundChanged(boolean isDataSaving) {
- final int N = mListeners.size();
- for (int i = 0; i < N; i++) {
- mListeners.get(i).onDataSaverChanged(isDataSaving);
+ synchronized (mListeners) {
+ for (int i = 0; i < mListeners.size(); i++) {
+ mListeners.get(i).onDataSaverChanged(isDataSaving);
+ }
}
}
public void addListener(Listener listener) {
- mListeners.add(listener);
- if (mListeners.size() == 1) {
- mPolicyManager.registerListener(mPolicyListener);
+ synchronized (mListeners) {
+ mListeners.add(listener);
+ if (mListeners.size() == 1) {
+ mPolicyManager.registerListener(mPolicyListener);
+ }
}
listener.onDataSaverChanged(isDataSaverEnabled());
}
public void remListener(Listener listener) {
- mListeners.remove(listener);
- if (mListeners.size() == 0) {
- mPolicyManager.unregisterListener(mPolicyListener);
+ synchronized (mListeners) {
+ mListeners.remove(listener);
+ if (mListeners.size() == 0) {
+ mPolicyManager.unregisterListener(mPolicyListener);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index a85fe0d..5046456 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -114,8 +114,8 @@
@Override
public String getProfileOwnerName() {
- for (UserInfo profile : mUserManager.getProfiles(mCurrentUserId)) {
- String name = mDevicePolicyManager.getProfileOwnerNameAsUser(profile.id);
+ for (int profileId : mUserManager.getProfileIdsWithDisabled(mCurrentUserId)) {
+ String name = mDevicePolicyManager.getProfileOwnerNameAsUser(profileId);
if (name != null) {
return name;
}
@@ -135,13 +135,13 @@
@Override
public String getProfileVpnName() {
- for (UserInfo profile : mUserManager.getProfiles(mVpnUserId)) {
- if (profile.id == mVpnUserId) {
+ for (int profileId : mUserManager.getProfileIdsWithDisabled(mVpnUserId)) {
+ if (profileId == mVpnUserId) {
continue;
}
- VpnConfig cfg = mCurrentVpns.get(profile.id);
+ VpnConfig cfg = mCurrentVpns.get(profileId);
if (cfg != null) {
- return getNameForVpnConfig(cfg, profile.getUserHandle());
+ return getNameForVpnConfig(cfg, UserHandle.of(profileId));
}
}
return null;
@@ -149,8 +149,8 @@
@Override
public boolean isVpnEnabled() {
- for (UserInfo profile : mUserManager.getProfiles(mVpnUserId)) {
- if (mCurrentVpns.get(profile.id) != null) {
+ for (int profileId : mUserManager.getProfileIdsWithDisabled(mVpnUserId)) {
+ if (mCurrentVpns.get(profileId) != null) {
return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 7c5cdfb..8b52bf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -3799,6 +3799,7 @@
} else {
getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
}
+ mContinuousShadowUpdate = continuousShadowUpdate;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 450001f..acef81b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -127,8 +127,7 @@
}
@Override
- protected boolean toggleSplitScreenMode() {
- return false;
+ protected void toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) {
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
index 748ee97..ae104cd 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
@@ -36,11 +36,15 @@
super.onCreate(savedInstanceState);
if (getFragmentManager().findFragmentByTag(TAG_TUNER) == null) {
+ boolean showDemoMode = getIntent().getAction().equals(
+ "com.android.settings.action.DEMO_MODE");
boolean showNightMode = getIntent().getBooleanExtra(
NightModeFragment.EXTRA_SHOW_NIGHT_MODE, false);
+ final PreferenceFragment fragment = showNightMode ? new NightModeFragment()
+ : showDemoMode ? new DemoModeFragment()
+ : new TunerFragment();
getFragmentManager().beginTransaction().replace(R.id.content_frame,
- showNightMode ? new NightModeFragment() : new TunerFragment(),
- TAG_TUNER).commit();
+ fragment, TAG_TUNER).commit();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
index e205ff5..011e159 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
@@ -16,6 +16,8 @@
package com.android.systemui.tv.pip;
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.Context;
@@ -50,9 +52,11 @@
private ImageView mGuideButtonPlayPauseImageView;
private final Runnable mHideGuideOverlayRunnable = new Runnable() {
public void run() {
- mGuideOverlayView.setVisibility(View.GONE);
+ mFadeOutAnimation.start();
}
};
+ private Animator mFadeInAnimation;
+ private Animator mFadeOutAnimation;
/**
* Shows PIP overlay UI only if it's not there.
@@ -74,11 +78,18 @@
setContentView(R.layout.tv_pip_overlay);
mGuideOverlayView = findViewById(R.id.guide_overlay);
mPipManager.addListener(this);
+ mFadeInAnimation = AnimatorInflater.loadAnimator(
+ this, R.anim.tv_pip_overlay_fade_in_animation);
+ mFadeInAnimation.setTarget(mGuideOverlayView);
+ mFadeOutAnimation = AnimatorInflater.loadAnimator(
+ this, R.anim.tv_pip_overlay_fade_out_animation);
+ mFadeOutAnimation.setTarget(mGuideOverlayView);
}
@Override
protected void onResume() {
super.onResume();
+ mFadeInAnimation.start();
mHandler.removeCallbacks(mHideGuideOverlayRunnable);
mHandler.postDelayed(mHideGuideOverlayRunnable, SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS);
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 180d918..2c53e29 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -16,6 +16,7 @@
package com.android.systemui.usb;
+import android.annotation.NonNull;
import android.app.Notification;
import android.app.Notification.Action;
import android.app.NotificationManager;
@@ -97,6 +98,11 @@
public void onDiskScanned(DiskInfo disk, int volumeCount) {
onDiskScannedInternal(disk, volumeCount);
}
+
+ @Override
+ public void onDiskDestroyed(DiskInfo disk) {
+ onDiskDestroyedInternal(disk);
+ }
};
private final BroadcastReceiver mSnoozeReceiver = new BroadcastReceiver() {
@@ -238,6 +244,15 @@
}
}
+ /**
+ * Remove all notifications for a disk when it goes away.
+ *
+ * @param disk The disk that went away.
+ */
+ private void onDiskDestroyedInternal(@NonNull DiskInfo disk) {
+ mNotificationManager.cancelAsUser(disk.getId(), DISK_ID, UserHandle.ALL);
+ }
+
private void onVolumeStateChangedInternal(VolumeInfo vol) {
switch (vol.getType()) {
case VolumeInfo.TYPE_PRIVATE:
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java b/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java
index 04640a2..d7c4bbf 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java
@@ -47,6 +47,7 @@
mAudioManager = audioManager;
getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+ setShowForAllUsers(true);
setMessage(mContext.getString(com.android.internal.R.string.safe_media_volume_warning));
setButton(DialogInterface.BUTTON_POSITIVE,
mContext.getString(com.android.internal.R.string.yes), this);
diff --git a/preloaded-classes b/preloaded-classes
index be645d2..d854769 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -12,6 +12,7 @@
[Landroid.animation.Keyframe;
[Landroid.animation.PropertyValuesHolder;
[Landroid.app.LoaderManagerImpl;
+[Landroid.app.Notification$Action;
[Landroid.content.ContentProviderResult;
[Landroid.content.ContentValues;
[Landroid.content.Intent;
@@ -29,7 +30,6 @@
[Landroid.content.res.Configuration;
[Landroid.content.res.StringBlock;
[Landroid.content.res.XmlBlock;
-[Landroid.database.Cursor;
[Landroid.database.CursorWindow;
[Landroid.database.sqlite.SQLiteConnection$Operation;
[Landroid.database.sqlite.SQLiteConnectionPool$AcquiredConnectionStatus;
@@ -54,7 +54,6 @@
[Landroid.graphics.drawable.GradientDrawable$Orientation;
[Landroid.graphics.drawable.LayerDrawable$ChildDrawable;
[Landroid.graphics.drawable.RippleForeground;
-[Landroid.hardware.display.WifiDisplay;
[Landroid.hardware.soundtrigger.SoundTrigger$ConfidenceLevel;
[Landroid.hardware.soundtrigger.SoundTrigger$Keyphrase;
[Landroid.hardware.soundtrigger.SoundTrigger$KeyphraseRecognitionExtra;
@@ -83,6 +82,10 @@
[Landroid.icu.util.ULocale$Category;
[Landroid.icu.util.ULocale;
[Landroid.media.AudioGain;
+[Landroid.media.MediaCodecInfo$CodecCapabilities;
+[Landroid.media.MediaCodecInfo$CodecProfileLevel;
+[Landroid.media.MediaCodecInfo$Feature;
+[Landroid.media.MediaCodecInfo;
[Landroid.net.Network;
[Landroid.net.NetworkInfo$DetailedState;
[Landroid.net.NetworkInfo$State;
@@ -109,7 +112,6 @@
[Landroid.text.method.TextKeyListener;
[Landroid.text.style.AlignmentSpan;
[Landroid.text.style.CharacterStyle;
-[Landroid.text.style.ClickableSpan;
[Landroid.text.style.LeadingMarginSpan;
[Landroid.text.style.LineBackgroundSpan;
[Landroid.text.style.LineHeightSpan;
@@ -122,12 +124,11 @@
[Landroid.text.style.URLSpan;
[Landroid.text.style.WrapTogetherSpan;
[Landroid.util.LongSparseArray;
-[Landroid.util.Pair;
+[Landroid.util.Range;
[Landroid.util.Rational;
[Landroid.view.Choreographer$CallbackQueue;
[Landroid.view.Display$ColorTransform;
[Landroid.view.Display$Mode;
-[Landroid.view.Display;
[Landroid.view.HandlerActionQueue$HandlerAction;
[Landroid.view.MenuItem;
[Landroid.view.View;
@@ -198,11 +199,13 @@
[Ljava.text.DateFormat$Field;
[Ljava.text.Normalizer$Form;
[Ljava.util.ArrayList;
+[Ljava.util.Comparators$NaturalOrderComparator;
[Ljava.util.Enumeration;
[Ljava.util.Formatter$Flags;
[Ljava.util.Formatter$FormatString;
[Ljava.util.HashMap$HashMapEntry;
[Ljava.util.Hashtable$HashtableEntry;
+[Ljava.util.List;
[Ljava.util.Locale$Category;
[Ljava.util.Locale;
[Ljava.util.Map$Entry;
@@ -216,11 +219,8 @@
[Ljava.util.regex.Pattern;
[Ljavax.crypto.Cipher$InitType;
[Ljavax.crypto.Cipher$NeedToSet;
-[Ljavax.microedition.khronos.egl.EGLConfig;
[Ljavax.net.ssl.KeyManager;
-[Ljavax.net.ssl.SSLSession;
[Ljavax.net.ssl.TrustManager;
-[Ljavax.security.auth.x500.X500Principal;
[Ljavax.security.cert.X509Certificate;
[Llibcore.io.ClassPathURLStreamHandler;
[Llibcore.reflect.AnnotationMember$DefaultValues;
@@ -307,12 +307,10 @@
android.animation.PathKeyframes$1
android.animation.PathKeyframes$2
android.animation.PathKeyframes$FloatKeyframesBase
-android.animation.PathKeyframes$IntKeyframesBase
android.animation.PathKeyframes$SimpleKeyframes
android.animation.PropertyValuesHolder
android.animation.PropertyValuesHolder$FloatPropertyValuesHolder
android.animation.PropertyValuesHolder$IntPropertyValuesHolder
-android.animation.PropertyValuesHolder$PropertyValues
android.animation.RectEvaluator
android.animation.StateListAnimator
android.animation.StateListAnimator$1
@@ -328,7 +326,6 @@
android.app.Activity$HostCallbacks
android.app.ActivityManager
android.app.ActivityManager$MemoryInfo
-android.app.ActivityManager$RecentTaskInfo
android.app.ActivityManager$RunningAppProcessInfo
android.app.ActivityManager$RunningAppProcessInfo$1
android.app.ActivityManager$StackId
@@ -348,7 +345,6 @@
android.app.ActivityThread$ApplicationThread
android.app.ActivityThread$BindServiceData
android.app.ActivityThread$ContextCleanupInfo
-android.app.ActivityThread$CreateBackupAgentData
android.app.ActivityThread$CreateServiceData
android.app.ActivityThread$DropBoxReporter
android.app.ActivityThread$EventLoggingReporter
@@ -381,7 +377,7 @@
android.app.ContextImpl
android.app.ContextImpl$ApplicationContentResolver
android.app.Dialog
-android.app.Dialog$1
+android.app.Dialog$-void__init__android_content_Context_context_int_themeResId_boolean_createContextThemeWrapper_LambdaImpl0
android.app.Dialog$ListenersHandler
android.app.DialogFragment
android.app.DownloadManager
@@ -402,8 +398,6 @@
android.app.IAlarmManager$Stub
android.app.IAlarmManager$Stub$Proxy
android.app.IApplicationThread
-android.app.IBackupAgent
-android.app.IBackupAgent$Stub
android.app.IInstrumentationWatcher
android.app.IInstrumentationWatcher$Stub
android.app.INotificationManager
@@ -415,6 +409,9 @@
android.app.ITransientNotification$Stub
android.app.IUiAutomationConnection
android.app.IUiAutomationConnection$Stub
+android.app.IUiModeManager
+android.app.IUiModeManager$Stub
+android.app.IUiModeManager$Stub$Proxy
android.app.Instrumentation
android.app.IntentReceiverLeaked
android.app.IntentService
@@ -437,16 +434,21 @@
android.app.Notification
android.app.Notification$1
android.app.Notification$Action
+android.app.Notification$Action$1
android.app.Notification$BigTextStyle
android.app.Notification$Builder
+android.app.Notification$BuilderRemoteViews
android.app.Notification$Style
android.app.NotificationManager
+android.app.OnActivityPausedListener
android.app.PendingIntent
android.app.PendingIntent$1
android.app.PendingIntent$CanceledException
android.app.QueuedWork
android.app.ReceiverRestrictedContext
android.app.ResourcesManager
+android.app.ResourcesManager$1
+android.app.ResourcesManager$ActivityResources
android.app.ResultInfo
android.app.ResultInfo$1
android.app.Service
@@ -530,11 +532,15 @@
android.app.SystemServiceRegistry$69
android.app.SystemServiceRegistry$7
android.app.SystemServiceRegistry$70
+android.app.SystemServiceRegistry$71
+android.app.SystemServiceRegistry$72
+android.app.SystemServiceRegistry$73
+android.app.SystemServiceRegistry$74
android.app.SystemServiceRegistry$8
android.app.SystemServiceRegistry$9
android.app.SystemServiceRegistry$CachedServiceFetcher
android.app.SystemServiceRegistry$ServiceFetcher
-android.app.SystemServiceRegistry$StaticOuterContextServiceFetcher
+android.app.SystemServiceRegistry$StaticApplicationContextServiceFetcher
android.app.SystemServiceRegistry$StaticServiceFetcher
android.app.UiModeManager
android.app.WallpaperManager
@@ -542,10 +548,9 @@
android.app.admin.IDevicePolicyManager
android.app.admin.IDevicePolicyManager$Stub
android.app.admin.IDevicePolicyManager$Stub$Proxy
-android.app.backup.BackupAgent
-android.app.backup.BackupAgent$BackupServiceBinder
-android.app.backup.BackupAgent$SharedPrefsSynchronizer
-android.app.backup.BackupAgentHelper
+android.app.admin.SecurityLog
+android.app.admin.SecurityLog$SecurityEvent
+android.app.admin.SecurityLog$SecurityEvent$1
android.app.backup.BackupDataInput
android.app.backup.BackupDataInput$EntityHeader
android.app.backup.BackupDataOutput
@@ -554,9 +559,6 @@
android.app.backup.FileBackupHelperBase
android.app.backup.FullBackup
android.app.backup.FullBackupDataOutput
-android.app.backup.IBackupManager
-android.app.backup.IBackupManager$Stub
-android.app.backup.IBackupManager$Stub$Proxy
android.app.job.JobScheduler
android.app.trust.ITrustManager
android.app.trust.ITrustManager$Stub
@@ -566,9 +568,6 @@
android.app.usage.UsageStatsManager
android.appwidget.AppWidgetManager
android.appwidget.AppWidgetProvider
-android.auditing.SecurityLog
-android.auditing.SecurityLog$SecurityEvent
-android.auditing.SecurityLog$SecurityEvent$1
android.bluetooth.BluetoothAdapter
android.bluetooth.BluetoothAdapter$1
android.bluetooth.BluetoothManager
@@ -585,6 +584,7 @@
android.content.ActivityNotFoundException
android.content.BroadcastReceiver
android.content.BroadcastReceiver$PendingResult
+android.content.BroadcastReceiver$PendingResult$1
android.content.ClipData
android.content.ClipData$1
android.content.ClipData$Item
@@ -600,7 +600,6 @@
android.content.ContentProviderClient
android.content.ContentProviderNative
android.content.ContentProviderOperation
-android.content.ContentProviderOperation$Builder
android.content.ContentProviderProxy
android.content.ContentProviderResult
android.content.ContentResolver
@@ -637,7 +636,6 @@
android.content.IntentSender
android.content.IntentSender$SendIntentException
android.content.OperationApplicationException
-android.content.PeriodicSync
android.content.RestrictionsManager
android.content.ServiceConnection
android.content.SharedPreferences
@@ -646,7 +644,6 @@
android.content.SyncContext
android.content.SyncRequest
android.content.SyncRequest$1
-android.content.SyncRequest$Builder
android.content.SyncResult
android.content.SyncResult$1
android.content.SyncStats
@@ -693,6 +690,7 @@
android.content.pm.ResolveInfo$1
android.content.pm.ServiceInfo
android.content.pm.ServiceInfo$1
+android.content.pm.ShortcutManager
android.content.pm.Signature
android.content.pm.Signature$1
android.content.pm.UserInfo
@@ -721,6 +719,8 @@
android.content.res.Resources$NotFoundException
android.content.res.Resources$Theme
android.content.res.Resources$ThemeKey
+android.content.res.ResourcesImpl
+android.content.res.ResourcesImpl$ThemeImpl
android.content.res.ResourcesKey
android.content.res.StringBlock
android.content.res.StringBlock$StyleIDs
@@ -762,7 +762,6 @@
android.database.IContentObserver$Stub$Proxy
android.database.MatrixCursor
android.database.MatrixCursor$RowBuilder
-android.database.MergeCursor
android.database.Observable
android.database.SQLException
android.database.sqlite.DatabaseObjectNotClosedException
@@ -775,7 +774,6 @@
android.database.sqlite.SQLiteConnectionPool
android.database.sqlite.SQLiteConnectionPool$AcquiredConnectionStatus
android.database.sqlite.SQLiteConnectionPool$ConnectionWaiter
-android.database.sqlite.SQLiteConstraintException
android.database.sqlite.SQLiteCursor
android.database.sqlite.SQLiteCursorDriver
android.database.sqlite.SQLiteCustomFunction
@@ -807,7 +805,6 @@
android.ddm.DdmHandleThread
android.ddm.DdmHandleViewDebug
android.ddm.DdmRegister
-android.graphics.AvoidXfermode
android.graphics.Bitmap
android.graphics.Bitmap$1
android.graphics.Bitmap$CompressFormat
@@ -820,10 +817,10 @@
android.graphics.Camera
android.graphics.Canvas
android.graphics.Canvas$EdgeType
+android.graphics.Canvas$NoImagePreloadHolder
android.graphics.CanvasProperty
android.graphics.Color
android.graphics.ColorFilter
-android.graphics.ColorMatrix
android.graphics.ColorMatrixColorFilter
android.graphics.ComposePathEffect
android.graphics.ComposeShader
@@ -859,6 +856,7 @@
android.graphics.Paint$FontMetrics
android.graphics.Paint$FontMetricsInt
android.graphics.Paint$Join
+android.graphics.Paint$NoImagePreloadHolder
android.graphics.Paint$Style
android.graphics.PaintFlagsDrawFilter
android.graphics.Path
@@ -869,7 +867,6 @@
android.graphics.PathMeasure
android.graphics.Picture
android.graphics.PixelFormat
-android.graphics.PixelXorXfermode
android.graphics.Point
android.graphics.Point$1
android.graphics.PointF
@@ -903,11 +900,14 @@
android.graphics.drawable.Animatable2
android.graphics.drawable.AnimatedStateListDrawable
android.graphics.drawable.AnimatedStateListDrawable$AnimatedStateListState
+android.graphics.drawable.AnimatedStateListDrawable$Transition
android.graphics.drawable.AnimatedVectorDrawable
android.graphics.drawable.AnimatedVectorDrawable$1
android.graphics.drawable.AnimatedVectorDrawable$AnimatedVectorDrawableState
android.graphics.drawable.AnimatedVectorDrawable$AnimatedVectorDrawableState$PendingAnimator
+android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimator
android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT
+android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorUI
android.graphics.drawable.AnimationDrawable
android.graphics.drawable.AnimationDrawable$AnimationState
android.graphics.drawable.BitmapDrawable
@@ -976,12 +976,14 @@
android.hardware.Camera$Face
android.hardware.ConsumerIrManager
android.hardware.Sensor
+android.hardware.SensorEvent
android.hardware.SensorEventListener
android.hardware.SensorManager
android.hardware.SerialManager
android.hardware.SerialPort
android.hardware.SystemSensorManager
android.hardware.SystemSensorManager$BaseEventQueue
+android.hardware.SystemSensorManager$SensorEventQueue
android.hardware.camera2.CameraCharacteristics$Key
android.hardware.camera2.CameraManager
android.hardware.camera2.CaptureRequest$Key
@@ -1000,12 +1002,6 @@
android.hardware.display.IDisplayManager$Stub$Proxy
android.hardware.display.IDisplayManagerCallback
android.hardware.display.IDisplayManagerCallback$Stub
-android.hardware.display.WifiDisplay
-android.hardware.display.WifiDisplay$1
-android.hardware.display.WifiDisplaySessionInfo
-android.hardware.display.WifiDisplaySessionInfo$1
-android.hardware.display.WifiDisplayStatus
-android.hardware.display.WifiDisplayStatus$1
android.hardware.fingerprint.FingerprintManager
android.hardware.hdmi.HdmiControlManager
android.hardware.input.IInputDevicesChangedListener
@@ -1021,6 +1017,10 @@
# android.hardware.location.ActivityRecognitionHardware
# android.hardware.location.IActivityRecognitionHardware
# android.hardware.location.IActivityRecognitionHardware$Stub
+android.hardware.location.ContextHubManager
+android.hardware.location.ContextHubService
+android.hardware.location.IContextHubService
+android.hardware.location.IContextHubService$Stub
android.hardware.radio.RadioManager
android.hardware.radio.RadioManager$AmBandConfig
android.hardware.radio.RadioManager$AmBandConfig$1
@@ -1046,6 +1046,7 @@
android.hardware.soundtrigger.SoundTrigger$ConfidenceLevel
android.hardware.soundtrigger.SoundTrigger$ConfidenceLevel$1
android.hardware.soundtrigger.SoundTrigger$GenericRecognitionEvent
+android.hardware.soundtrigger.SoundTrigger$GenericRecognitionEvent$1
android.hardware.soundtrigger.SoundTrigger$GenericSoundModel
android.hardware.soundtrigger.SoundTrigger$Keyphrase
android.hardware.soundtrigger.SoundTrigger$Keyphrase$1
@@ -1070,7 +1071,6 @@
android.hardware.usb.IUsbManager$Stub$Proxy
android.hardware.usb.UsbDevice
android.hardware.usb.UsbDeviceConnection
-android.hardware.usb.UsbInterface
android.hardware.usb.UsbManager
android.hardware.usb.UsbRequest
android.icu.impl.BMPSet
@@ -1396,6 +1396,7 @@
android.media.AudioDevicePort
android.media.AudioDevicePortConfig
android.media.AudioFormat
+android.media.AudioFormat$1
android.media.AudioGain
android.media.AudioGainConfig
android.media.AudioHandle
@@ -1412,8 +1413,6 @@
android.media.AudioPortConfig
android.media.AudioPortEventHandler
android.media.AudioRecord
-android.media.AudioRoutesInfo
-android.media.AudioRoutesInfo$1
android.media.AudioRouting
android.media.AudioSystem
android.media.AudioTimestamp
@@ -1425,21 +1424,13 @@
android.media.ExifInterface
android.media.IAudioFocusDispatcher
android.media.IAudioFocusDispatcher$Stub
-android.media.IAudioRoutesObserver
-android.media.IAudioRoutesObserver$Stub
android.media.IAudioService
android.media.IAudioService$Stub
android.media.IAudioService$Stub$Proxy
android.media.IMediaHTTPConnection
android.media.IMediaHTTPConnection$Stub
-android.media.IMediaRouterClient
-android.media.IMediaRouterClient$Stub
-android.media.IMediaRouterService
-android.media.IMediaRouterService$Stub
android.media.IRecordingConfigDispatcher
android.media.IRecordingConfigDispatcher$Stub
-android.media.IRemoteVolumeObserver
-android.media.IRemoteVolumeObserver$Stub
android.media.Image
android.media.ImageReader
android.media.ImageReader$SurfaceImage
@@ -1447,10 +1438,18 @@
android.media.ImageWriter$WriterSurfaceImage
android.media.JetPlayer
android.media.MediaCodec
+android.media.MediaCodecInfo
+android.media.MediaCodecInfo$AudioCapabilities
+android.media.MediaCodecInfo$CodecCapabilities
+android.media.MediaCodecInfo$CodecProfileLevel
+android.media.MediaCodecInfo$EncoderCapabilities
+android.media.MediaCodecInfo$Feature
+android.media.MediaCodecInfo$VideoCapabilities
android.media.MediaCodecList
android.media.MediaCrypto
android.media.MediaDrm
android.media.MediaExtractor
+android.media.MediaFormat
android.media.MediaHTTPConnection
android.media.MediaMetadataRetriever
android.media.MediaMuxer
@@ -1461,17 +1460,6 @@
android.media.MediaPlayer$OnSeekCompleteListener
android.media.MediaRecorder
android.media.MediaRouter
-android.media.MediaRouter$Callback
-android.media.MediaRouter$RouteCategory
-android.media.MediaRouter$RouteInfo
-android.media.MediaRouter$RouteInfo$1
-android.media.MediaRouter$Static
-android.media.MediaRouter$Static$1
-android.media.MediaRouter$Static$Client
-android.media.MediaRouter$VolumeChangeReceiver
-android.media.MediaRouter$WifiDisplayStatusChangedReceiver
-android.media.MediaRouterClientState
-android.media.MediaRouterClientState$1
android.media.MediaScanner
android.media.MediaSync
android.media.PlaybackParams
@@ -1479,9 +1467,10 @@
android.media.RemoteDisplay
android.media.ResampleInputStream
android.media.SubtitleController$Listener
-android.media.ThumbnailUtils
android.media.ToneGenerator
-android.media.audiofx.AcousticEchoCanceler
+android.media.Utils
+android.media.Utils$1
+android.media.Utils$2
android.media.audiofx.AudioEffect
android.media.audiopolicy.AudioMix
android.media.audiopolicy.AudioMixingRule
@@ -1504,13 +1493,13 @@
android.net.ConnectivityManager
android.net.ConnectivityManager$CallbackHandler
android.net.ConnectivityManager$NetworkCallback
+android.net.ConnectivityThread
android.net.Credentials
-android.net.DhcpResults
-android.net.DhcpResults$1
android.net.EthernetManager
android.net.IConnectivityManager
android.net.IConnectivityManager$Stub
android.net.IConnectivityManager$Stub$Proxy
+android.net.INetworkPolicyManager
android.net.IpPrefix
android.net.IpPrefix$1
android.net.LinkAddress
@@ -1545,8 +1534,6 @@
android.net.SSLCertificateSocketFactory
android.net.SSLCertificateSocketFactory$1
android.net.SSLSessionCache
-android.net.StaticIpConfiguration
-android.net.StaticIpConfiguration$1
android.net.TrafficStats
android.net.Uri
android.net.Uri$1
@@ -1584,10 +1571,16 @@
android.nfc.INfcAdapter$Stub$Proxy
android.nfc.INfcCardEmulation
android.nfc.INfcCardEmulation$Stub
+android.nfc.INfcCardEmulation$Stub$Proxy
android.nfc.INfcFCardEmulation
android.nfc.INfcFCardEmulation$Stub
+android.nfc.INfcFCardEmulation$Stub$Proxy
android.nfc.INfcTag
android.nfc.INfcTag$Stub
+android.nfc.INfcTag$Stub$Proxy
+android.nfc.NfcActivityManager
+android.nfc.NfcAdapter
+android.nfc.NfcAdapter$1
android.nfc.NfcManager
android.opengl.EGL14
android.opengl.EGLConfig
@@ -1631,9 +1624,8 @@
android.os.CancellationSignal$OnCancelListener
android.os.CancellationSignal$Transport
android.os.ConditionVariable
-android.os.CpuUsageInfo
-android.os.CpuUsageInfo$1
android.os.DeadObjectException
+android.os.DeadSystemException
android.os.Debug
android.os.Debug$MemoryInfo
android.os.Debug$MemoryInfo$1
@@ -1665,6 +1657,8 @@
android.os.IUserManager
android.os.IUserManager$Stub
android.os.IUserManager$Stub$Proxy
+android.os.IVibratorService
+android.os.IVibratorService$Stub
android.os.Looper
android.os.MemoryFile
android.os.Message
@@ -1679,10 +1673,13 @@
android.os.ParcelFileDescriptor
android.os.ParcelFileDescriptor$1
android.os.ParcelFileDescriptor$AutoCloseInputStream
+android.os.ParcelFileDescriptor$AutoCloseOutputStream
android.os.ParcelUuid
android.os.Parcelable
android.os.Parcelable$ClassLoaderCreator
android.os.Parcelable$Creator
+android.os.ParcelableParcel
+android.os.ParcelableParcel$1
android.os.PatternMatcher
android.os.PatternMatcher$1
android.os.PersistableBundle
@@ -1691,6 +1688,7 @@
android.os.PowerManager$WakeLock
android.os.PowerManager$WakeLock$1
android.os.Process
+android.os.RecoverySystem
android.os.RemoteException
android.os.ResultReceiver
android.os.SELinux
@@ -1726,24 +1724,26 @@
android.os.StrictMode$VmPolicy$Builder
android.os.SystemClock
android.os.SystemProperties
+android.os.SystemVibrator
android.os.Trace
android.os.Trace$1
-android.os.TransactionTooLargeException
android.os.UEventObserver
android.os.UserHandle
android.os.UserHandle$1
android.os.UserManager
android.os.Vibrator
android.os.ZygoteStartFailedEx
+android.os.health.SystemHealthManager
android.os.storage.IMountService
android.os.storage.IMountService$Stub
android.os.storage.IMountService$Stub$Proxy
+android.os.storage.IObbActionListener
+android.os.storage.IObbActionListener$Stub
android.os.storage.StorageManager
+android.os.storage.StorageManager$ObbActionListener
android.os.storage.StorageVolume
android.os.storage.StorageVolume$1
-android.preference.Preference$OnPreferenceChangeListener
android.preference.PreferenceActivity
-android.preference.PreferenceFragment
android.preference.PreferenceFragment$OnPreferenceStartFragmentCallback
android.preference.PreferenceManager
android.preference.PreferenceManager$OnPreferenceTreeClickListener
@@ -1836,7 +1836,6 @@
android.system.UnixSocketAddress
android.telecom.TelecomManager
android.telephony.CarrierConfigManager
-android.telephony.PhoneNumberUtils
android.telephony.Rlog
android.telephony.SubscriptionManager
android.telephony.TelephonyManager
@@ -1852,12 +1851,15 @@
android.text.GetChars
android.text.GraphicsOperations
android.text.Html
+android.text.Html$HtmlParser
+android.text.HtmlToSpannedConverter
android.text.Hyphenator
android.text.InputFilter
android.text.InputType
android.text.Layout
android.text.Layout$Alignment
android.text.Layout$Directions
+android.text.Layout$Ellipsizer
android.text.MeasuredText
android.text.NoCopySpan
android.text.NoCopySpan$Concrete
@@ -1916,7 +1918,6 @@
android.text.method.SingleLineTransformationMethod
android.text.method.TextKeyListener
android.text.method.TextKeyListener$Capitalize
-android.text.method.Touch
android.text.method.TransformationMethod
android.text.method.TransformationMethod2
android.text.style.AlignmentSpan
@@ -1937,6 +1938,7 @@
android.text.style.StyleSpan
android.text.style.SuggestionSpan
android.text.style.TabStopSpan
+android.text.style.TextAppearanceSpan
android.text.style.URLSpan
android.text.style.UnderlineSpan
android.text.style.UpdateAppearance
@@ -1985,7 +1987,6 @@
android.util.EventLog$Event
android.util.FloatProperty
android.util.IntProperty
-android.util.JsonReader
android.util.LocaleList
android.util.LocaleList$1
android.util.Log
@@ -1993,7 +1994,6 @@
android.util.Log$ImmediateLogWriter
android.util.Log$TerribleFailureHandler
android.util.LogPrinter
-android.util.LongArray
android.util.LongSparseArray
android.util.LongSparseLongArray
android.util.LruCache
@@ -2006,15 +2006,16 @@
android.util.Pair
android.util.PathParser
android.util.PathParser$PathData
-android.util.Patterns
android.util.Pools$Pool
android.util.Pools$SimplePool
android.util.Pools$SynchronizedPool
android.util.Printer
android.util.Property
+android.util.Range
android.util.Rational
android.util.Singleton
android.util.Size
+android.util.SizeF
android.util.Slog
android.util.SparseArray
android.util.SparseBooleanArray
@@ -2031,6 +2032,7 @@
android.view.AbsSavedState$2
android.view.ActionMode
android.view.ActionMode$Callback
+android.view.ActionMode$Callback2
android.view.ActionProvider
android.view.ActionProvider$SubUiVisibilityListener
android.view.Choreographer
@@ -2135,7 +2137,6 @@
android.view.RenderNode
android.view.RenderNodeAnimator
android.view.RenderNodeAnimator$1
-android.view.RenderNodeAnimatorSetHelper
android.view.SearchEvent
android.view.SoundEffectConstants
android.view.SubMenu
@@ -2151,6 +2152,7 @@
android.view.SurfaceSession
android.view.SurfaceView
android.view.TextureView
+android.view.TextureView$SurfaceTextureListener
android.view.ThreadedRenderer
android.view.ThreadedRenderer$HardwareDrawCallbacks
android.view.ThreadedRenderer$ProcessInitializer
@@ -2174,6 +2176,7 @@
android.view.View$AttachInfo$Callbacks
android.view.View$BaseSavedState
android.view.View$BaseSavedState$1
+android.view.View$CheckForLongPress
android.view.View$CheckForTap
android.view.View$ForegroundInfo
android.view.View$ListenerInfo
@@ -2258,10 +2261,10 @@
android.view.WindowCallbacks
android.view.WindowContentFrameStats
android.view.WindowContentFrameStats$1
-android.view.WindowId
android.view.WindowInsets
android.view.WindowLeaked
android.view.WindowManager
+android.view.WindowManager$BadTokenException
android.view.WindowManager$LayoutParams
android.view.WindowManager$LayoutParams$1
android.view.WindowManagerGlobal
@@ -2280,6 +2283,7 @@
android.view.accessibility.AccessibilityRecord
android.view.accessibility.CaptioningManager
android.view.accessibility.CaptioningManager$1
+android.view.accessibility.CaptioningManager$CaptionStyle
android.view.accessibility.CaptioningManager$CaptioningChangeListener
android.view.accessibility.CaptioningManager$MyContentObserver
android.view.accessibility.IAccessibilityManager
@@ -2295,6 +2299,7 @@
android.view.animation.Animation$2
android.view.animation.Animation$3
android.view.animation.Animation$AnimationListener
+android.view.animation.Animation$Description
android.view.animation.AnimationSet
android.view.animation.AnimationUtils
android.view.animation.BaseInterpolator
@@ -2316,6 +2321,7 @@
android.view.inputmethod.ExtractedText
android.view.inputmethod.ExtractedText$1
android.view.inputmethod.InputConnection
+android.view.inputmethod.InputConnectionInspector
android.view.inputmethod.InputMethodManager
android.view.inputmethod.InputMethodManager$1
android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper
@@ -2323,15 +2329,17 @@
android.view.inputmethod.InputMethodManager$H
android.view.inputmethod.InputMethodManager$ImeInputEventSender
android.view.inputmethod.InputMethodManager$PendingEvent
-android.view.inputmethod.InputMethodSubtype
android.view.textservice.SpellCheckerSession$SpellCheckerSessionListener
android.view.textservice.SpellCheckerSubtype
android.view.textservice.SpellCheckerSubtype$1
android.view.textservice.TextServicesManager
+android.webkit.IWebViewUpdateService
+android.webkit.IWebViewUpdateService$Stub
android.webkit.MimeTypeMap
android.webkit.URLUtil
android.webkit.WebSettings
android.webkit.WebView
+android.webkit.WebViewClient
android.webkit.WebViewFactory
android.webkit.WebViewFactory$MissingWebViewPackageException
android.widget.AbsListView
@@ -2348,6 +2356,7 @@
android.widget.AbsListView$WindowRunnnable
android.widget.AbsSeekBar
android.widget.AbsSpinner
+android.widget.AbsSpinner$RecycleBin
android.widget.AbsoluteLayout
android.widget.ActionMenuPresenter
android.widget.ActionMenuPresenter$1
@@ -2362,13 +2371,9 @@
android.widget.AdapterView
android.widget.AdapterView$AdapterDataSetObserver
android.widget.AdapterView$OnItemClickListener
-android.widget.AdapterView$OnItemLongClickListener
android.widget.AdapterView$OnItemSelectedListener
android.widget.ArrayAdapter
android.widget.AutoCompleteTextView
-android.widget.AutoCompleteTextView$DropDownItemClickListener
-android.widget.AutoCompleteTextView$MyWatcher
-android.widget.AutoCompleteTextView$PassThroughClickListener
android.widget.BaseAdapter
android.widget.Button
android.widget.CheckBox
@@ -2381,15 +2386,17 @@
android.widget.Editor
android.widget.Editor$1
android.widget.Editor$2
-android.widget.Editor$3
android.widget.Editor$Blink
android.widget.Editor$CursorAnchorInfoNotifier
+android.widget.Editor$CursorController
android.widget.Editor$EditOperation
android.widget.Editor$EditOperation$1
android.widget.Editor$InputContentType
android.widget.Editor$InputMethodState
+android.widget.Editor$InsertionPointCursorController
android.widget.Editor$PositionListener
android.widget.Editor$ProcessTextIntentActionsHandler
+android.widget.Editor$SelectionModifierCursorController
android.widget.Editor$SpanController
android.widget.Editor$SuggestionHelper
android.widget.Editor$SuggestionHelper$SuggestionSpanComparator
@@ -2439,6 +2446,19 @@
android.widget.RelativeLayout$DependencyGraph$Node
android.widget.RelativeLayout$LayoutParams
android.widget.RemoteViews
+android.widget.RemoteViews$1
+android.widget.RemoteViews$2
+android.widget.RemoteViews$3
+android.widget.RemoteViews$Action
+android.widget.RemoteViews$ActionException
+android.widget.RemoteViews$BitmapCache
+android.widget.RemoteViews$LayoutParamAction
+android.widget.RemoteViews$MemoryUsageCounter
+android.widget.RemoteViews$MutablePair
+android.widget.RemoteViews$OnClickHandler
+android.widget.RemoteViews$ReflectionAction
+android.widget.RemoteViews$RuntimeAction
+android.widget.RemoteViews$SetDrawableParameters
android.widget.RemoteViewsAdapter$RemoteAdapterConnectionCallback
android.widget.RtlSpacingHelper
android.widget.ScrollBarDrawable
@@ -2451,7 +2471,10 @@
android.widget.SpellChecker
android.widget.SpellChecker$SpellParser
android.widget.Spinner
+android.widget.Spinner$SpinnerPopup
android.widget.SpinnerAdapter
+android.widget.Switch
+android.widget.Switch$1
android.widget.TextView
android.widget.TextView$3
android.widget.TextView$BufferType
@@ -2460,6 +2483,7 @@
android.widget.TextView$Drawables
android.widget.TextView$OnEditorActionListener
android.widget.TextView$SavedState
+android.widget.TextView$SavedState$1
android.widget.ThemedSpinnerAdapter
android.widget.Toast
android.widget.Toast$TN
@@ -2470,7 +2494,6 @@
android.widget.Toolbar$2
android.widget.Toolbar$ExpandedActionViewMenuPresenter
android.widget.Toolbar$LayoutParams
-android.widget.ViewAnimator
android.widget.WrapperListAdapter
com.android.dex.Annotation
com.android.dex.ClassData
@@ -2511,9 +2534,10 @@
com.android.internal.app.IAppOpsService
com.android.internal.app.IAppOpsService$Stub
com.android.internal.app.IAppOpsService$Stub$Proxy
+com.android.internal.app.IBatteryStats
+com.android.internal.app.IBatteryStats$Stub
com.android.internal.app.IVoiceInteractor
com.android.internal.app.IVoiceInteractor$Stub
-com.android.internal.app.WindowDecorActionBar
com.android.internal.appwidget.IAppWidgetService
com.android.internal.appwidget.IAppWidgetService$Stub
com.android.internal.appwidget.IAppWidgetService$Stub$Proxy
@@ -2521,6 +2545,8 @@
com.android.internal.content.ReferrerIntent
com.android.internal.content.ReferrerIntent$1
com.android.internal.inputmethod.InputMethodUtils
+com.android.internal.inputmethod.InputMethodUtils$1
+com.android.internal.inputmethod.LocaleUtils$LocaleExtractor
com.android.internal.logging.AndroidConfig
com.android.internal.logging.AndroidHandler
com.android.internal.logging.AndroidHandler$1
@@ -2575,6 +2601,9 @@
com.android.internal.util.FastXmlSerializer
com.android.internal.util.GrowingArrayUtils
com.android.internal.util.LineBreakBufferedWriter
+com.android.internal.util.MessageUtils
+com.android.internal.util.NotificationColorUtil
+com.android.internal.util.NotificationColorUtil$ColorUtilsFromCompat
com.android.internal.util.Preconditions
com.android.internal.util.VirtualRefBasePtr
com.android.internal.util.XmlUtils
@@ -2613,13 +2642,9 @@
com.android.internal.view.menu.MenuPresenter$Callback
com.android.internal.view.menu.MenuView
com.android.internal.view.menu.ShowableListMenu
-com.android.internal.widget.ActionBarOverlayLayout$ActionBarVisibilityCallback
-com.android.internal.widget.AlertDialogLayout
com.android.internal.widget.BackgroundFallback
-com.android.internal.widget.ButtonBarLayout
com.android.internal.widget.DecorContentParent
com.android.internal.widget.DecorToolbar
-com.android.internal.widget.DialogTitle
com.android.internal.widget.EditableInputConnection
com.android.internal.widget.ScrollBarUtils
com.android.internal.widget.ToolbarWidgetWrapper
@@ -2884,7 +2909,6 @@
dalvik.system.CloseGuard$DefaultReporter
dalvik.system.CloseGuard$Reporter
dalvik.system.DalvikLogHandler
-dalvik.system.DalvikLogging
dalvik.system.DexClassLoader
dalvik.system.DexFile
dalvik.system.DexFile$DFEnum
@@ -2911,6 +2935,7 @@
java.io.ByteArrayOutputStream
java.io.CharArrayWriter
java.io.Closeable
+java.io.Console
java.io.DataInput
java.io.DataInputStream
java.io.DataOutput
@@ -2922,15 +2947,14 @@
java.io.Externalizable
java.io.File
java.io.File$PathStatus
-java.io.File$TempDirectory
java.io.FileDescriptor
+java.io.FileFilter
java.io.FileInputStream
java.io.FileInputStream$UseManualSkipException
java.io.FileNotFoundException
java.io.FileOutputStream
java.io.FileReader
java.io.FileSystem
-java.io.FileWriter
java.io.FilenameFilter
java.io.FilterInputStream
java.io.FilterOutputStream
@@ -2941,7 +2965,6 @@
java.io.InputStreamReader
java.io.InterruptedIOException
java.io.InvalidObjectException
-java.io.NotSerializableException
java.io.ObjectInput
java.io.ObjectInputStream
java.io.ObjectInputStream$BlockDataInputStream
@@ -3013,6 +3036,7 @@
java.lang.EnumConstantNotPresentException
java.lang.Error
java.lang.Exception
+java.lang.ExceptionInInitializerError
java.lang.Float
java.lang.FloatingDecimal
java.lang.FloatingDecimal$1
@@ -3056,7 +3080,6 @@
java.lang.RuntimeException
java.lang.RuntimePermission
java.lang.SecurityException
-java.lang.SecurityManager
java.lang.Short
java.lang.Short$ShortCache
java.lang.Shutdown
@@ -3133,7 +3156,6 @@
java.math.BigInt
java.math.BigInteger
java.math.BitLevel
-java.math.Multiplication
java.math.NativeBN
java.math.RoundingMode
java.net.AbstractPlainDatagramSocketImpl
@@ -3260,22 +3282,18 @@
java.security.AlgorithmParameters
java.security.AlgorithmParametersSpi
java.security.BasicPermission
-java.security.BasicPermissionCollection
java.security.CryptoPrimitive
java.security.GeneralSecurityException
java.security.Guard
java.security.InvalidAlgorithmParameterException
java.security.InvalidKeyException
-java.security.InvalidParameterException
java.security.Key
java.security.KeyException
java.security.KeyFactory
java.security.KeyFactorySpi
java.security.KeyManagementException
-java.security.KeyPair
java.security.KeyStore
java.security.KeyStore$1
-java.security.KeyStore$LoadStoreParameter
java.security.KeyStoreException
java.security.KeyStoreSpi
java.security.MessageDigest
@@ -3285,6 +3303,7 @@
java.security.NoSuchProviderException
java.security.Permission
java.security.PermissionCollection
+java.security.Permissions
java.security.Principal
java.security.PrivateKey
java.security.PrivilegedAction
@@ -3332,7 +3351,6 @@
java.security.cert.PKIXRevocationChecker
java.security.cert.PolicyNode
java.security.cert.TrustAnchor
-java.security.cert.X509CRL
java.security.cert.X509CertSelector
java.security.cert.X509Certificate
java.security.cert.X509Extension
@@ -3342,7 +3360,6 @@
java.security.interfaces.ECPrivateKey
java.security.interfaces.ECPublicKey
java.security.interfaces.RSAKey
-java.security.interfaces.RSAPrivateCrtKey
java.security.interfaces.RSAPrivateKey
java.security.interfaces.RSAPublicKey
java.security.spec.AlgorithmParameterSpec
@@ -3351,19 +3368,14 @@
java.security.spec.ECFieldFp
java.security.spec.ECParameterSpec
java.security.spec.ECPoint
-java.security.spec.ECPrivateKeySpec
java.security.spec.ECPublicKeySpec
java.security.spec.EllipticCurve
java.security.spec.EncodedKeySpec
java.security.spec.InvalidKeySpecException
java.security.spec.InvalidParameterSpecException
java.security.spec.KeySpec
-java.security.spec.PKCS8EncodedKeySpec
-java.security.spec.RSAPrivateCrtKeySpec
-java.security.spec.RSAPrivateKeySpec
java.security.spec.RSAPublicKeySpec
java.security.spec.X509EncodedKeySpec
-java.sql.Timestamp
java.text.AttributedCharacterIterator$Attribute
java.text.CalendarBuilder
java.text.Collator
@@ -3401,7 +3413,9 @@
java.util.AbstractSequentialList
java.util.AbstractSet
java.util.ArrayDeque
+java.util.ArrayDeque$DeqIterator
java.util.ArrayList
+java.util.ArrayList$ArrayListSpliterator
java.util.ArrayList$Itr
java.util.ArrayList$ListItr
java.util.ArrayList$SubList
@@ -3414,6 +3428,7 @@
java.util.Collections
java.util.Collections$1
java.util.Collections$2
+java.util.Collections$3
java.util.Collections$AsLIFOQueue
java.util.Collections$CheckedCollection
java.util.Collections$CheckedList
@@ -3456,6 +3471,14 @@
java.util.Collections$UnmodifiableSortedSet
java.util.ComparableTimSort
java.util.Comparator
+java.util.Comparator$-java_util_Comparator_comparingDouble_java_util_function_ToDoubleFunction_keyExtractor_LambdaImpl0
+java.util.Comparator$-java_util_Comparator_comparingInt_java_util_function_ToIntFunction_keyExtractor_LambdaImpl0
+java.util.Comparator$-java_util_Comparator_comparingLong_java_util_function_ToLongFunction_keyExtractor_LambdaImpl0
+java.util.Comparator$-java_util_Comparator_comparing_java_util_function_Function_keyExtractor_LambdaImpl0
+java.util.Comparator$-java_util_Comparator_comparing_java_util_function_Function_keyExtractor_java_util_Comparator_keyComparator_LambdaImpl0
+java.util.Comparator$-java_util_Comparator_thenComparing_java_util_Comparator_other_LambdaImpl0
+java.util.Comparators$NaturalOrderComparator
+java.util.Comparators$NullComparator
java.util.ConcurrentModificationException
java.util.Currency
java.util.Date
@@ -3464,7 +3487,6 @@
java.util.DualPivotQuicksort
java.util.EnumMap
java.util.EnumMap$1
-java.util.EnumMap$EnumMapIterator
java.util.EnumSet
java.util.Enumeration
java.util.EventListener
@@ -3497,8 +3519,6 @@
java.util.Hashtable$KeySet
java.util.Hashtable$ValueCollection
java.util.IdentityHashMap
-java.util.IdentityHashMap$EntryIterator
-java.util.IdentityHashMap$EntrySet
java.util.IdentityHashMap$IdentityHashMapIterator
java.util.IdentityHashMap$KeySet
java.util.IllegalFormatException
@@ -3538,7 +3558,6 @@
java.util.RandomAccess
java.util.RandomAccessSubList
java.util.RegularEnumSet
-java.util.RegularEnumSet$EnumSetIterator
java.util.ResourceBundle
java.util.ResourceBundle$1
java.util.ResourceBundle$BundleReference
@@ -3550,7 +3569,6 @@
java.util.ResourceBundle$LoaderReference
java.util.ResourceBundle$RBClassLoader
java.util.ResourceBundle$RBClassLoader$1
-java.util.Scanner
java.util.ServiceLoader
java.util.ServiceLoader$1
java.util.ServiceLoader$LazyIterator
@@ -3558,6 +3576,17 @@
java.util.SimpleTimeZone
java.util.SortedMap
java.util.SortedSet
+java.util.Spliterator
+java.util.Spliterator$OfDouble
+java.util.Spliterator$OfInt
+java.util.Spliterator$OfLong
+java.util.Spliterator$OfPrimitive
+java.util.Spliterators
+java.util.Spliterators$EmptySpliterator
+java.util.Spliterators$EmptySpliterator$OfDouble
+java.util.Spliterators$EmptySpliterator$OfInt
+java.util.Spliterators$EmptySpliterator$OfLong
+java.util.Spliterators$EmptySpliterator$OfRef
java.util.Stack
java.util.StringTokenizer
java.util.SubList
@@ -3569,12 +3598,10 @@
java.util.TimerTask
java.util.TimerThread
java.util.TreeMap
-java.util.TreeMap$AscendingSubMap
java.util.TreeMap$EntryIterator
java.util.TreeMap$EntrySet
java.util.TreeMap$KeyIterator
java.util.TreeMap$KeySet
-java.util.TreeMap$NavigableSubMap
java.util.TreeMap$PrivateEntryIterator
java.util.TreeMap$TreeMapEntry
java.util.TreeMap$ValueIterator
@@ -3595,14 +3622,12 @@
java.util.WeakHashMap$Values
java.util.XMLUtils
java.util.concurrent.AbstractExecutorService
-java.util.concurrent.ArrayBlockingQueue
java.util.concurrent.BlockingQueue
java.util.concurrent.Callable
java.util.concurrent.CancellationException
java.util.concurrent.ConcurrentHashMap
java.util.concurrent.ConcurrentHashMap$BaseIterator
java.util.concurrent.ConcurrentHashMap$CollectionView
-java.util.concurrent.ConcurrentHashMap$CounterCell
java.util.concurrent.ConcurrentHashMap$ForwardingNode
java.util.concurrent.ConcurrentHashMap$KeyIterator
java.util.concurrent.ConcurrentHashMap$KeySetView
@@ -3611,8 +3636,6 @@
java.util.concurrent.ConcurrentHashMap$Traverser
java.util.concurrent.ConcurrentHashMap$TreeBin
java.util.concurrent.ConcurrentHashMap$TreeNode
-java.util.concurrent.ConcurrentHashMap$ValueIterator
-java.util.concurrent.ConcurrentHashMap$ValuesView
java.util.concurrent.ConcurrentLinkedQueue
java.util.concurrent.ConcurrentLinkedQueue$Node
java.util.concurrent.ConcurrentMap
@@ -3689,6 +3712,16 @@
java.util.concurrent.locks.ReentrantReadWriteLock$Sync
java.util.concurrent.locks.ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter
java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock
+java.util.function.BiConsumer
+java.util.function.BiFunction
+java.util.function.Consumer
+java.util.function.Function
+java.util.function.Predicate
+java.util.function.Supplier
+java.util.function.ToDoubleFunction
+java.util.function.ToIntFunction
+java.util.function.ToLongFunction
+java.util.function.UnaryOperator
java.util.jar.JarEntry
java.util.jar.JarFile
java.util.jar.JarFile$1
@@ -3709,15 +3742,19 @@
java.util.logging.LogManager$LoggerWeakRef
java.util.logging.LogManager$RootLogger
java.util.logging.LogManager$SystemLoggerContext
-java.util.logging.LogRecord
java.util.logging.Logger
java.util.logging.LoggingPermission
java.util.logging.LoggingProxyImpl
+java.util.prefs.AbstractPreferences
+java.util.prefs.Preferences
java.util.regex.MatchResult
java.util.regex.Matcher
java.util.regex.Pattern
java.util.regex.PatternSyntaxException
java.util.spi.LocaleServiceProvider
+java.util.stream.BaseStream
+java.util.stream.Stream
+java.util.stream.StreamSupport
java.util.zip.Adler32
java.util.zip.CRC32
java.util.zip.CheckedInputStream
@@ -3748,7 +3785,6 @@
javax.crypto.CipherSpi
javax.crypto.IllegalBlockSizeException
javax.crypto.JceSecurity
-javax.crypto.Mac
javax.crypto.NoSuchPaddingException
javax.crypto.NullCipher
javax.crypto.SecretKey
@@ -3757,10 +3793,6 @@
javax.crypto.spec.SecretKeySpec
javax.microedition.khronos.egl.EGL
javax.microedition.khronos.egl.EGL10
-javax.microedition.khronos.egl.EGLConfig
-javax.microedition.khronos.egl.EGLContext
-javax.microedition.khronos.egl.EGLDisplay
-javax.microedition.khronos.egl.EGLSurface
javax.microedition.khronos.opengles.GL
javax.microedition.khronos.opengles.GL10
javax.microedition.khronos.opengles.GL10Ext
@@ -3771,7 +3803,6 @@
javax.net.ServerSocketFactory
javax.net.SocketFactory
javax.net.ssl.ExtendedSSLSession
-javax.net.ssl.HandshakeCompletedEvent
javax.net.ssl.HandshakeCompletedListener
javax.net.ssl.HostnameVerifier
javax.net.ssl.HttpsURLConnection
@@ -3779,23 +3810,20 @@
javax.net.ssl.KeyManagerFactory
javax.net.ssl.KeyManagerFactory$1
javax.net.ssl.KeyManagerFactorySpi
+javax.net.ssl.SNIHostName
+javax.net.ssl.SNIServerName
javax.net.ssl.SSLContext
javax.net.ssl.SSLContextSpi
javax.net.ssl.SSLEngine
javax.net.ssl.SSLException
-javax.net.ssl.SSLHandshakeException
javax.net.ssl.SSLParameters
javax.net.ssl.SSLPeerUnverifiedException
javax.net.ssl.SSLProtocolException
-javax.net.ssl.SSLServerSocket
javax.net.ssl.SSLServerSocketFactory
javax.net.ssl.SSLSession
-javax.net.ssl.SSLSessionBindingEvent
-javax.net.ssl.SSLSessionBindingListener
javax.net.ssl.SSLSessionContext
javax.net.ssl.SSLSocket
javax.net.ssl.SSLSocketFactory
-javax.net.ssl.SSLSocketFactory$1
javax.net.ssl.TrustManager
javax.net.ssl.TrustManagerFactory
javax.net.ssl.TrustManagerFactory$1
@@ -3841,7 +3869,6 @@
libcore.io.NioBufferIterator
libcore.io.Os
libcore.io.Posix
-libcore.math.MathUtils
libcore.net.NetworkSecurityPolicy
libcore.net.NetworkSecurityPolicy$DefaultNetworkSecurityPolicy
libcore.net.UriCodec
@@ -3857,7 +3884,6 @@
libcore.reflect.ListOfTypes
libcore.reflect.ListOfVariables
libcore.reflect.ParameterizedTypeImpl
-libcore.reflect.TypeVariableImpl
libcore.reflect.Types
libcore.util.BasicLruCache
libcore.util.CharsetUtils
@@ -3866,7 +3892,6 @@
libcore.util.NativeAllocationRegistry
libcore.util.NativeAllocationRegistry$CleanerRunner
libcore.util.NativeAllocationRegistry$CleanerThunk
-libcore.util.Objects
libcore.util.ZoneInfo
libcore.util.ZoneInfo$CheckedArithmeticException
libcore.util.ZoneInfo$WallTime
@@ -3892,11 +3917,8 @@
org.apache.http.HttpResponse
org.apache.http.HttpVersion
org.apache.http.NameValuePair
-org.apache.http.ProtocolException
org.apache.http.ProtocolVersion
-org.apache.http.ReasonPhraseCatalog
org.apache.http.StatusLine
-org.apache.http.client.ClientProtocolException
org.apache.http.client.HttpClient
org.apache.http.client.ResponseHandler
org.apache.http.client.methods.AbortableHttpRequest
@@ -3905,26 +3927,15 @@
org.apache.http.client.methods.HttpPost
org.apache.http.client.methods.HttpRequestBase
org.apache.http.client.methods.HttpUriRequest
-org.apache.http.client.utils.URLEncodedUtils
org.apache.http.conn.ClientConnectionManager
org.apache.http.conn.ConnectTimeoutException
-org.apache.http.conn.params.ConnManagerPNames
-org.apache.http.conn.params.ConnManagerParams
-org.apache.http.conn.params.ConnManagerParams$1
-org.apache.http.conn.params.ConnPerRoute
-org.apache.http.conn.scheme.LayeredSocketFactory
-org.apache.http.conn.scheme.SocketFactory
org.apache.http.entity.AbstractHttpEntity
-org.apache.http.entity.BasicHttpEntity
org.apache.http.entity.ByteArrayEntity
-org.apache.http.impl.client.EntityEnclosingRequestWrapper
-org.apache.http.impl.client.RequestWrapper
org.apache.http.impl.cookie.DateParseException
org.apache.http.impl.cookie.DateUtils
org.apache.http.message.AbstractHttpMessage
org.apache.http.message.BasicHeader
org.apache.http.message.BasicHttpResponse
-org.apache.http.message.BasicNameValuePair
org.apache.http.message.BasicStatusLine
org.apache.http.message.HeaderGroup
org.apache.http.params.AbstractHttpParams
@@ -3933,6 +3944,18 @@
org.apache.http.params.HttpConnectionParams
org.apache.http.params.HttpParams
org.apache.http.protocol.HttpContext
+org.ccil.cowan.tagsoup.AttributesImpl
+org.ccil.cowan.tagsoup.AutoDetector
+org.ccil.cowan.tagsoup.Element
+org.ccil.cowan.tagsoup.ElementType
+org.ccil.cowan.tagsoup.HTMLModels
+org.ccil.cowan.tagsoup.HTMLScanner
+org.ccil.cowan.tagsoup.HTMLSchema
+org.ccil.cowan.tagsoup.Parser
+org.ccil.cowan.tagsoup.Parser$1
+org.ccil.cowan.tagsoup.ScanHandler
+org.ccil.cowan.tagsoup.Scanner
+org.ccil.cowan.tagsoup.Schema
org.json.JSON
org.json.JSONArray
org.json.JSONException
@@ -3954,6 +3977,7 @@
org.xml.sax.SAXNotRecognizedException
org.xml.sax.SAXNotSupportedException
org.xml.sax.XMLReader
+org.xml.sax.ext.LexicalHandler
org.xml.sax.helpers.DefaultHandler
org.xmlpull.v1.XmlPullParser
org.xmlpull.v1.XmlPullParserException
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index ea3cffe..afea7f3 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2138,6 +2138,20 @@
// Settings > Apps > Gear > Special Access > Premium SMS access
PREMIUM_SMS_ACCESS = 388;
+ // Logged when the user resizes the docked stack. Arguments:
+ // 0: Split 50:50
+ // 1: Docked smaller
+ // 2: Docked larger
+ ACTION_WINDOW_DOCK_RESIZE = 389;
+
+ // User exits split-screen by dragging the divider to the side of the screen. Arguments
+ // 0: Docked gets maximized
+ // 1: Fullscreen gets maximized
+ ACTION_WINDOW_UNDOCK_MAX = 390;
+
+ // User tried to dock an unresizable app.
+ ACTION_WINDOW_DOCK_UNRESIZABLE = 391;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index 9ec6e8d..04ea8e5 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -58,6 +58,7 @@
private static final int MAX_NUMBER_IO_INPUT_ALLOC = 16;
Type mType;
+ boolean mOwningType = false;
Bitmap mBitmap;
int mUsage;
Allocation mAdaptedAllocation;
@@ -383,13 +384,16 @@
guard.open("destroy");
}
- Allocation(long id, RenderScript rs, Type t, int usage, MipmapControl mips) {
+ Allocation(long id, RenderScript rs, Type t, boolean owningType, int usage, MipmapControl mips) {
this(id, rs, t, usage);
+ mOwningType = owningType;
mMipmapControl = mips;
}
protected void finalize() throws Throwable {
RenderScript.registerNativeFree.invoke(RenderScript.sRuntime, mSize);
+ // Set mType null to avoid double-destroying it in case its finalizer races ahead
+ mType = null;
super.finalize();
}
@@ -1578,6 +1582,9 @@
mRS.finish(); // Necessary because resize is fifoed and update is async.
long typeID = mRS.nAllocationGetType(getID(mRS));
+ // Sets zero the mID so that the finalizer of the old mType value won't
+ // destroy the native object that is being reused.
+ mType.setID(0);
mType = new Type(typeID, mRS);
mType.updateFromNative();
updateCacheInfo(mType);
@@ -1921,7 +1928,7 @@
if (id == 0) {
throw new RSRuntimeException("Allocation creation failed.");
}
- return new Allocation(id, rs, type, usage, mips);
+ return new Allocation(id, rs, type, false, usage, mips);
} finally {
Trace.traceEnd(RenderScript.TRACE_TAG);
}
@@ -1979,7 +1986,7 @@
if (id == 0) {
throw new RSRuntimeException("Allocation creation failed.");
}
- return new Allocation(id, rs, t, usage, MipmapControl.MIPMAP_NONE);
+ return new Allocation(id, rs, t, true, usage, MipmapControl.MIPMAP_NONE);
} finally {
Trace.traceEnd(RenderScript.TRACE_TAG);
}
@@ -2068,7 +2075,7 @@
}
// keep a reference to the Bitmap around to prevent GC
- Allocation alloc = new Allocation(id, rs, t, usage, mips);
+ Allocation alloc = new Allocation(id, rs, t, true, usage, mips);
alloc.setBitmap(b);
return alloc;
}
@@ -2078,7 +2085,7 @@
if (id == 0) {
throw new RSRuntimeException("Load failed.");
}
- return new Allocation(id, rs, t, usage, mips);
+ return new Allocation(id, rs, t, true, usage, mips);
} finally {
Trace.traceEnd(RenderScript.TRACE_TAG);
}
@@ -2150,7 +2157,7 @@
}
for (int i=1; i<numAlloc; i++) {
- mAllocationArray[i] = createFromAllcation(rs, mAllocationArray[0]);
+ mAllocationArray[i] = createFromAllocation(rs, mAllocationArray[0]);
}
return mAllocationArray;
} finally {
@@ -2169,7 +2176,7 @@
* @param alloc RenderScript Allocation describing data layout.
* @return Allocation sharing the same data structure.
*/
- static Allocation createFromAllcation(RenderScript rs, Allocation alloc) {
+ static Allocation createFromAllocation(RenderScript rs, Allocation alloc) {
try {
Trace.traceBegin(RenderScript.TRACE_TAG, "createFromAllcation");
rs.validate();
@@ -2184,7 +2191,7 @@
if (id == 0) {
throw new RSRuntimeException("Allocation creation failed.");
}
- Allocation outAlloc = new Allocation(id, rs, type, usage, mips);
+ Allocation outAlloc = new Allocation(id, rs, type, false, usage, mips);
if ((usage & USAGE_IO_INPUT) != 0) {
outAlloc.shareBufferQueue(alloc);
}
@@ -2360,7 +2367,7 @@
if(id == 0) {
throw new RSRuntimeException("Load failed for bitmap " + b + " element " + e);
}
- return new Allocation(id, rs, t, usage, mips);
+ return new Allocation(id, rs, t, true, usage, mips);
}
/**
@@ -2605,6 +2612,13 @@
if((mUsage & USAGE_IO_OUTPUT) != 0) {
setSurface(null);
}
+
+ if (mType != null && mOwningType) {
+ mType.destroy();
+ mType = null;
+ }
+
super.destroy();
}
+
}
diff --git a/rs/java/android/renderscript/ScriptGroup.java b/rs/java/android/renderscript/ScriptGroup.java
index 219f16b..35ae8b4 100644
--- a/rs/java/android/renderscript/ScriptGroup.java
+++ b/rs/java/android/renderscript/ScriptGroup.java
@@ -187,6 +187,23 @@
guard.open("destroy");
}
+ /**
+ * Destroys this Closure and the Allocation for its return value
+ */
+ public void destroy() {
+ super.destroy();
+ if (mReturnValue != null) {
+ mReturnValue.destroy();
+ }
+ }
+
+ protected void finalize() throws Throwable {
+ // Set null mReturnValue to avoid double-destroying it, in case its
+ // finalizer races ahead.
+ mReturnValue = null;
+ super.finalize();
+ }
+
private void retrieveValueAndDependenceInfo(RenderScript rs,
int index, Script.FieldID fid, Object obj,
long[] values, int[] sizes,
@@ -1015,6 +1032,8 @@
throw new RSIllegalArgumentException("invalid script group name");
}
ScriptGroup ret = new ScriptGroup(mRS, name, mClosures, mInputs, outputs);
+ mClosures = new ArrayList<Closure>();
+ mInputs = new ArrayList<Input>();
return ret;
}
@@ -1042,4 +1061,20 @@
}
+ /**
+ * Destroy this ScriptGroup and all Closures in it
+ */
+ public void destroy() {
+ super.destroy();
+ for(Closure c : mClosures) {
+ c.destroy();
+ }
+ }
+
+ protected void finalize() throws Throwable {
+ // Clear out the list mClosures to avoid double-destroying the closures,
+ // in case their finalizers race ahead.
+ mClosures.clear();
+ super.finalize();
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index fb1ef37..b2196bf 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -156,10 +156,10 @@
final float offsetY = sentSpec.offsetY;
// Compute the new center and update spec as needed.
- final float centerX = (mMagnifiedBounds.width() / 2.0f - offsetX) / scale
- + mMagnifiedBounds.left;
- final float centerY = (mMagnifiedBounds.height() / 2.0f - offsetY) / scale
- + mMagnifiedBounds.top;
+ final float centerX = (mMagnifiedBounds.width() / 2.0f
+ + mMagnifiedBounds.left - offsetX) / scale;
+ final float centerY = (mMagnifiedBounds.height() / 2.0f
+ + mMagnifiedBounds.top - offsetY) / scale;
if (updateSpec) {
setScaleAndCenter(scale, centerX, centerY, false);
} else {
@@ -256,7 +256,7 @@
public float getCenterX() {
synchronized (mLock) {
return (mMagnifiedBounds.width() / 2.0f
- - getOffsetX()) / getScale() + mMagnifiedBounds.left;
+ + mMagnifiedBounds.left - getOffsetX()) / getScale();
}
}
@@ -279,7 +279,7 @@
public float getCenterY() {
synchronized (mLock) {
return (mMagnifiedBounds.height() / 2.0f
- - getOffsetY()) / getScale() + mMagnifiedBounds.top;
+ + mMagnifiedBounds.top - getOffsetY()) / getScale();
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index f93fb1b..6ca3af8 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -460,12 +460,9 @@
}
synchronized (mLock) {
reloadWidgetsMaskedState(userId);
- List<UserInfo> profiles = mUserManager.getEnabledProfiles(userId);
- if (profiles != null) {
- for (int i = 0; i < profiles.size(); i++) {
- UserInfo user = profiles.get(i);
- reloadWidgetsMaskedState(user.id);
- }
+ int[] profileIds = mUserManager.getEnabledProfileIds(userId);
+ for (int profileId : profileIds) {
+ reloadWidgetsMaskedState(profileId);
}
}
}
@@ -3458,33 +3455,12 @@
public int[] getEnabledGroupProfileIds(int userId) {
final int parentId = getGroupParent(userId);
- final List<UserInfo> profiles;
final long identity = Binder.clearCallingIdentity();
try {
- profiles = mUserManager.getProfiles(parentId);
+ return mUserManager.getEnabledProfileIds(parentId);
} finally {
Binder.restoreCallingIdentity(identity);
}
-
- int enabledProfileCount = 0;
- final int profileCount = profiles.size();
- for (int i = 0; i < profileCount; i++) {
- if (profiles.get(i).isEnabled()) {
- enabledProfileCount++;
- }
- }
-
- int enabledProfileIndex = 0;
- final int[] profileIds = new int[enabledProfileCount];
- for (int i = 0; i < profileCount; i++) {
- UserInfo profile = profiles.get(i);
- if (profile.isEnabled()) {
- profileIds[enabledProfileIndex] = profile.getUserHandle().getIdentifier();
- enabledProfileIndex++;
- }
- }
-
- return profileIds;
}
public void enforceServiceExistsAndRequiresBindRemoteViewsPermission(
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 966deb6..2ccc3fe 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -116,6 +116,7 @@
import com.android.internal.net.VpnProfile;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.MessageUtils;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
@@ -164,7 +165,7 @@
implements PendingIntent.OnFinished {
private static final String TAG = "ConnectivityService";
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private static final boolean VDBG = false;
private static final boolean LOGD_RULES = false;
@@ -224,6 +225,9 @@
private static final int ENABLED = 1;
private static final int DISABLED = 0;
+ private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
+ new Class[] { AsyncChannel.class, ConnectivityService.class, NetworkAgent.class });
+
private enum ReapUnvalidatedNetworks {
// Tear down networks that have no chance (e.g. even if validated) of becoming
// the highest scoring network satisfying a NetworkRequest. This should be passed when
@@ -446,9 +450,8 @@
*/
private class LegacyTypeTracker {
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private static final boolean VDBG = false;
- private static final String TAG = "CSLegacyTypeTracker";
/**
* Array of lists, one per legacy network type (e.g., TYPE_MOBILE_MMS).
@@ -601,12 +604,6 @@
pw.decreaseIndent();
pw.println();
}
-
- // This class needs its own log method because it has a different TAG.
- private void log(String s) {
- Slog.d(TAG, s);
- }
-
}
private LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker();
@@ -1528,7 +1525,7 @@
mInitialBroadcast = new Intent(intent);
}
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- if (DBG) {
+ if (VDBG) {
log("sendStickyBroadcast: action=" + intent.getAction());
}
@@ -1656,7 +1653,7 @@
}
if (LinkProperties.isValidMtu(mtu, newLp.hasGlobalIPv6Address()) == false) {
- loge("Unexpected mtu value: " + mtu + ", " + iface);
+ if (mtu != 0) loge("Unexpected mtu value: " + mtu + ", " + iface);
return;
}
@@ -1667,7 +1664,7 @@
}
try {
- if (DBG) log("Setting MTU size: " + iface + ", " + mtu);
+ if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
mNetd.setMtu(iface, mtu);
} catch (Exception e) {
Slog.e(TAG, "exception in setMtu()" + e);
@@ -1703,7 +1700,7 @@
if (tcpBufferSizes.equals(mCurrentTcpBufferSizes)) return;
try {
- if (DBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes);
+ if (VDBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes);
final String prefix = "/sys/kernel/ipv4/tcp_";
FileUtils.stringToFile(prefix + "rmem_min", values[0]);
@@ -1900,11 +1897,12 @@
}
}
- private boolean isLiveNetworkAgent(NetworkAgentInfo nai, String msg) {
+ private boolean isLiveNetworkAgent(NetworkAgentInfo nai, int what) {
if (nai.network == null) return false;
final NetworkAgentInfo officialNai = getNetworkAgentInfoForNetwork(nai.network);
if (officialNai != null && officialNai.equals(nai)) return true;
if (officialNai != null || VDBG) {
+ final String msg = sMagicDecoderRing.get(what, Integer.toString(what));
loge(msg + " - isLiveNetworkAgent found mismatched netId: " + officialNai +
" - " + nai);
}
@@ -1921,10 +1919,10 @@
super(looper);
}
- @Override
- public void handleMessage(Message msg) {
- NetworkInfo info;
+ private boolean maybeHandleAsyncChannelMessage(Message msg) {
switch (msg.what) {
+ default:
+ return false;
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
handleAsyncChannelHalfConnect(msg);
break;
@@ -1938,69 +1936,58 @@
handleAsyncChannelDisconnected(msg);
break;
}
+ }
+ return true;
+ }
+
+ private void maybeHandleNetworkAgentMessage(Message msg) {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ if (VDBG) {
+ final String what = sMagicDecoderRing.get(msg.what, Integer.toString(msg.what));
+ log(String.format("%s from unknown NetworkAgent", what));
+ }
+ return;
+ }
+
+ switch (msg.what) {
case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
- NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
- if (nai == null) {
- loge("EVENT_NETWORK_CAPABILITIES_CHANGED from unknown NetworkAgent");
- } else {
- final NetworkCapabilities networkCapabilities =
- (NetworkCapabilities)msg.obj;
- if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL) ||
- networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
- Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
- }
- if (nai.created && !nai.networkCapabilities.equalImmutableCapabilities(
- networkCapabilities)) {
- Slog.wtf(TAG, "BUG: " + nai + " changed immutable capabilities: "
- + nai.networkCapabilities + " -> " + networkCapabilities);
- }
- updateCapabilities(nai, networkCapabilities);
+ final NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
+ if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL) ||
+ networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
+ Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
}
+ if (nai.created && !nai.networkCapabilities.equalImmutableCapabilities(
+ networkCapabilities)) {
+ Slog.wtf(TAG, "BUG: " + nai + " changed immutable capabilities: "
+ + nai.networkCapabilities + " -> " + networkCapabilities);
+ }
+ updateCapabilities(nai, networkCapabilities);
break;
}
case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
- NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
- if (nai == null) {
- loge("NetworkAgent not found for EVENT_NETWORK_PROPERTIES_CHANGED");
- } else {
- if (VDBG) {
- log("Update of LinkProperties for " + nai.name() +
- "; created=" + nai.created);
- }
- LinkProperties oldLp = nai.linkProperties;
- synchronized (nai) {
- nai.linkProperties = (LinkProperties)msg.obj;
- }
- if (nai.created) updateLinkProperties(nai, oldLp);
+ if (VDBG) {
+ log("Update of LinkProperties for " + nai.name() +
+ "; created=" + nai.created);
}
+ LinkProperties oldLp = nai.linkProperties;
+ synchronized (nai) {
+ nai.linkProperties = (LinkProperties)msg.obj;
+ }
+ if (nai.created) updateLinkProperties(nai, oldLp);
break;
}
case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
- NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
- if (nai == null) {
- loge("EVENT_NETWORK_INFO_CHANGED from unknown NetworkAgent");
- break;
- }
- info = (NetworkInfo) msg.obj;
+ NetworkInfo info = (NetworkInfo) msg.obj;
updateNetworkInfo(nai, info);
break;
}
case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: {
- NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
- if (nai == null) {
- loge("EVENT_NETWORK_SCORE_CHANGED from unknown NetworkAgent");
- break;
- }
Integer score = (Integer) msg.obj;
if (score != null) updateNetworkScore(nai, score.intValue());
break;
}
case NetworkAgent.EVENT_UID_RANGES_ADDED: {
- NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
- if (nai == null) {
- loge("EVENT_UID_RANGES_ADDED from unknown NetworkAgent");
- break;
- }
try {
mNetd.addVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
} catch (Exception e) {
@@ -2010,11 +1997,6 @@
break;
}
case NetworkAgent.EVENT_UID_RANGES_REMOVED: {
- NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
- if (nai == null) {
- loge("EVENT_UID_RANGES_REMOVED from unknown NetworkAgent");
- break;
- }
try {
mNetd.removeVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
} catch (Exception e) {
@@ -2024,11 +2006,6 @@
break;
}
case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: {
- NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
- if (nai == null) {
- loge("EVENT_SET_EXPLICITLY_SELECTED from unknown NetworkAgent");
- break;
- }
if (nai.created && !nai.networkMisc.explicitlySelected) {
loge("ERROR: created network explicitly selected.");
}
@@ -2037,20 +2014,26 @@
break;
}
case NetworkAgent.EVENT_PACKET_KEEPALIVE: {
- NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
- if (nai == null) {
- loge("EVENT_PACKET_KEEPALIVE from unknown NetworkAgent");
- break;
- }
mKeepaliveTracker.handleEventPacketKeepalive(nai, msg);
break;
}
+ }
+ }
+
+ private boolean maybeHandleNetworkMonitorMessage(Message msg) {
+ switch (msg.what) {
+ default:
+ return false;
case NetworkMonitor.EVENT_NETWORK_TESTED: {
- NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
- if (isLiveNetworkAgent(nai, "EVENT_NETWORK_TESTED")) {
+ final NetworkAgentInfo nai;
+ synchronized (mNetworkForNetId) {
+ nai = mNetworkForNetId.get(msg.arg2);
+ }
+ if (nai != null) {
final boolean valid =
(msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
- if (DBG) log(nai.name() + " validation " + (valid ? " passed" : "failed"));
+ if (DBG) log(nai.name() + " validation " + (valid ? "passed" : "failed") +
+ (msg.obj == null ? "" : " with redirect to " + (String)msg.obj));
if (valid != nai.lastValidated) {
final int oldScore = nai.getCurrentScore();
nai.lastValidated = valid;
@@ -2061,16 +2044,18 @@
}
updateInetCondition(nai);
// Let the NetworkAgent know the state of its network
+ Bundle redirectUrlBundle = new Bundle();
+ redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, (String)msg.obj);
nai.asyncChannel.sendMessage(
- android.net.NetworkAgent.CMD_REPORT_NETWORK_STATUS,
+ NetworkAgent.CMD_REPORT_NETWORK_STATUS,
(valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
- 0, null);
+ 0, redirectUrlBundle);
}
break;
}
case NetworkMonitor.EVENT_NETWORK_LINGER_COMPLETE: {
NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
- if (isLiveNetworkAgent(nai, "EVENT_NETWORK_LINGER_COMPLETE")) {
+ if (isLiveNetworkAgent(nai, msg.what)) {
handleLingerComplete(nai);
}
break;
@@ -2102,6 +2087,14 @@
break;
}
}
+ return true;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (!maybeHandleAsyncChannelMessage(msg) && !maybeHandleNetworkMonitorMessage(msg)) {
+ maybeHandleNetworkAgentMessage(msg);
+ }
}
}
@@ -2321,7 +2314,7 @@
if (DBG) log("Attempt to release unowned NetworkRequest " + request);
return;
}
- if (DBG) log("releasing NetworkRequest " + request);
+ if (VDBG || (DBG && nri.isRequest())) log("releasing NetworkRequest " + request);
nri.unlinkDeathRecipient();
mNetworkRequests.remove(request);
mNetworkRequestInfoLogs.log("RELEASE " + nri);
@@ -2334,7 +2327,7 @@
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
if (nai.networkRequests.get(nri.request.requestId) != null) {
nai.networkRequests.remove(nri.request.requestId);
- if (DBG) {
+ if (VDBG) {
log(" Removing from current network " + nai.name() +
", leaving " + nai.networkRequests.size() +
" requests.");
@@ -2445,14 +2438,14 @@
}
private void scheduleUnvalidatedPrompt(NetworkAgentInfo nai) {
- if (DBG) log("scheduleUnvalidatedPrompt " + nai.network);
+ if (VDBG) log("scheduleUnvalidatedPrompt " + nai.network);
mHandler.sendMessageDelayed(
mHandler.obtainMessage(EVENT_PROMPT_UNVALIDATED, nai.network),
PROMPT_UNVALIDATED_DELAY_MS);
}
private void handlePromptUnvalidated(Network network) {
- if (DBG) log("handlePromptUnvalidated " + network);
+ if (VDBG) log("handlePromptUnvalidated " + network);
NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
// Only prompt if the network is unvalidated and was explicitly selected by the user, and if
@@ -2495,11 +2488,14 @@
break;
}
}
- if (msg.what == EVENT_EXPIRE_NET_TRANSITION_WAKELOCK) {
- log("Failed to find a new network - expiring NetTransition Wakelock");
- } else {
- log("NetTransition Wakelock (" + (causedBy == null ? "unknown" : causedBy) +
- " cleared because we found a replacement network");
+ if (VDBG) {
+ if (msg.what == EVENT_EXPIRE_NET_TRANSITION_WAKELOCK) {
+ log("Failed to find a new network - expiring NetTransition Wakelock");
+ } else {
+ log("NetTransition Wakelock (" +
+ (causedBy == null ? "unknown" : causedBy) +
+ " cleared because we found a replacement network");
+ }
}
break;
}
@@ -3353,10 +3349,6 @@
private static enum NotificationType { SIGN_IN, NO_INTERNET; };
private void setProvNotificationVisible(boolean visible, int networkType, String action) {
- if (DBG) {
- log("setProvNotificationVisible: E visible=" + visible + " networkType=" + networkType
- + " action=" + action);
- }
Intent intent = new Intent(action);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
// Concatenate the range of types onto the range of NetIDs.
@@ -3383,7 +3375,7 @@
private void setProvNotificationVisibleIntent(boolean visible, int id,
NotificationType notifyType, int networkType, String extraInfo, PendingIntent intent,
boolean highPriority) {
- if (DBG) {
+ if (VDBG || (DBG && visible)) {
log("setProvNotificationVisibleIntent " + notifyType + " visible=" + visible
+ " networkType=" + getNetworkTypeName(networkType)
+ " extraInfo=" + extraInfo + " highPriority=" + highPriority);
@@ -3826,8 +3818,7 @@
Bundle thresholds = new Bundle();
thresholds.putIntegerArrayList("thresholds", thresholdsArray);
- // TODO: Switch to VDBG.
- if (DBG) {
+ if (VDBG || (DBG && !"CONNECT".equals(reason))) {
String detail;
if (request != null && request.networkCapabilities.hasSignalStrength()) {
detail = reason + " " + request.networkCapabilities.getSignalStrength();
@@ -3992,7 +3983,7 @@
new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId());
NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
NetworkRequestType.LISTEN);
- if (DBG) log("listenForNetwork for " + nri);
+ if (VDBG) log("listenForNetwork for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
return networkRequest;
@@ -4010,7 +4001,7 @@
new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId());
NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation,
NetworkRequestType.LISTEN);
- if (DBG) log("pendingListenForNetwork for " + nri);
+ if (VDBG) log("pendingListenForNetwork for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
}
@@ -4215,7 +4206,7 @@
// do this twice, adding non-nexthop routes first, then routes they are dependent on
for (RouteInfo route : routeDiff.added) {
if (route.hasGateway()) continue;
- if (DBG) log("Adding Route [" + route + "] to network " + netId);
+ if (VDBG) log("Adding Route [" + route + "] to network " + netId);
try {
mNetd.addRoute(netId, route);
} catch (Exception e) {
@@ -4226,7 +4217,7 @@
}
for (RouteInfo route : routeDiff.added) {
if (route.hasGateway() == false) continue;
- if (DBG) log("Adding Route [" + route + "] to network " + netId);
+ if (VDBG) log("Adding Route [" + route + "] to network " + netId);
try {
mNetd.addRoute(netId, route);
} catch (Exception e) {
@@ -4237,7 +4228,7 @@
}
for (RouteInfo route : routeDiff.removed) {
- if (DBG) log("Removing Route [" + route + "] from network " + netId);
+ if (VDBG) log("Removing Route [" + route + "] from network " + netId);
try {
mNetd.removeRoute(netId, route);
} catch (Exception e) {
@@ -4253,7 +4244,7 @@
}
Collection<InetAddress> dnses = newLp.getDnsServers();
- if (DBG) log("Setting Dns servers for network " + netId + " to " + dnses);
+ if (DBG) log("Setting DNS servers for network " + netId + " to " + dnses);
try {
mNetd.setDnsServersForNetwork(
netId, NetworkUtils.makeStrings(dnses), newLp.getDomains());
@@ -4521,14 +4512,14 @@
}
if (currentNetwork == null ||
currentNetwork.getCurrentScore() < newNetwork.getCurrentScore()) {
- if (DBG) log("rematch for " + newNetwork.name());
+ if (VDBG) log("rematch for " + newNetwork.name());
if (currentNetwork != null) {
- if (DBG) log(" accepting network in place of " + currentNetwork.name());
+ if (VDBG) log(" accepting network in place of " + currentNetwork.name());
currentNetwork.networkRequests.remove(nri.request.requestId);
currentNetwork.networkLingered.add(nri.request);
affectedNetworks.add(currentNetwork);
} else {
- if (DBG) log(" accepting network in place of null");
+ if (VDBG) log(" accepting network in place of null");
}
unlinger(newNetwork);
mNetworkForRequestId.put(nri.request.requestId, newNetwork);
@@ -4858,7 +4849,7 @@
}
private void updateNetworkScore(NetworkAgentInfo nai, int score) {
- if (DBG) log("updateNetworkScore for " + nai.name() + " to " + score);
+ if (VDBG) log("updateNetworkScore for " + nai.name() + " to " + score);
if (score < 0) {
loge("updateNetworkScore for " + nai.name() + " got a negative score (" + score +
"). Bumping score to min of 0");
@@ -4936,7 +4927,7 @@
}
protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) {
- if (DBG) log("notifyType " + notifyTypeToName(notifyType) + " for " + networkAgent.name());
+ if (VDBG) log("notifyType " + notifyTypeToName(notifyType) + " for " + networkAgent.name());
for (int i = 0; i < networkAgent.networkRequests.size(); i++) {
NetworkRequest nr = networkAgent.networkRequests.valueAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index aa98648..5a90488 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -58,7 +58,6 @@
* as a camera launch.
*/
private static final long CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS = 300;
- private static final long CAMERA_POWER_DOUBLE_TAP_MIN_TIME_MS = 120;
/** The listener that receives the gesture event. */
private final GestureEventListener mGestureListener = new GestureEventListener();
@@ -260,8 +259,7 @@
synchronized (this) {
doubleTapInterval = event.getEventTime() - mLastPowerDown;
if (mCameraDoubleTapPowerEnabled
- && doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
- && doubleTapInterval > CAMERA_POWER_DOUBLE_TAP_MIN_TIME_MS) {
+ && doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
launched = true;
intercept = interactive;
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 58e3674..544e645 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -66,7 +66,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -1050,12 +1049,8 @@
}
void updateCurrentProfileIds() {
- List<UserInfo> profiles = mUserManager.getProfiles(mSettings.getCurrentUserId());
- int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
- for (int i = 0; i < currentProfileIds.length; i++) {
- currentProfileIds[i] = profiles.get(i).id;
- }
- mSettings.setCurrentProfileIds(currentProfileIds);
+ mSettings.setCurrentProfileIds(
+ mUserManager.getProfileIdsWithDisabled(mSettings.getCurrentUserId()));
}
@Override
@@ -2971,16 +2966,6 @@
return;
}
setInputMethodLocked(nextSubtype.mImi.getId(), nextSubtype.mSubtypeId);
- if (mSubtypeSwitchedByShortCutToast != null) {
- mSubtypeSwitchedByShortCutToast.cancel();
- mSubtypeSwitchedByShortCutToast = null;
- }
- if ((mImeWindowVis & InputMethodService.IME_VISIBLE) != 0) {
- // IME window is shown. The user should be able to visually understand that the
- // subtype is changed in most of cases. To avoid UI overlap, we do not show a toast
- // in this case.
- return;
- }
final InputMethodInfo newInputMethodInfo = mMethodMap.get(mCurMethodId);
if (newInputMethodInfo == null) {
return;
@@ -2988,8 +2973,12 @@
final CharSequence toastText = InputMethodUtils.getImeAndSubtypeDisplayName(mContext,
newInputMethodInfo, mCurrentSubtype);
if (!TextUtils.isEmpty(toastText)) {
- mSubtypeSwitchedByShortCutToast = Toast.makeText(mContext, toastText.toString(),
- Toast.LENGTH_SHORT);
+ if (mSubtypeSwitchedByShortCutToast == null) {
+ mSubtypeSwitchedByShortCutToast = Toast.makeText(mContext, toastText,
+ Toast.LENGTH_SHORT);
+ } else {
+ mSubtypeSwitchedByShortCutToast.setText(toastText);
+ }
mSubtypeSwitchedByShortCutToast.show();
}
}
@@ -3649,6 +3638,7 @@
private static final String ATTR_ID = "id";
private static final String ATTR_LABEL = "label";
private static final String ATTR_ICON = "icon";
+ private static final String ATTR_IME_SUBTYPE_ID = "subtypeId";
private static final String ATTR_IME_SUBTYPE_LOCALE = "imeSubtypeLocale";
private static final String ATTR_IME_SUBTYPE_LANGUAGE_TAG = "languageTag";
private static final String ATTR_IME_SUBTYPE_MODE = "imeSubtypeMode";
@@ -3742,6 +3732,10 @@
for (int i = 0; i < N; ++i) {
final InputMethodSubtype subtype = subtypesList.get(i);
out.startTag(null, NODE_SUBTYPE);
+ if (subtype.hasSubtypeId()) {
+ out.attribute(null, ATTR_IME_SUBTYPE_ID,
+ String.valueOf(subtype.getSubtypeId()));
+ }
out.attribute(null, ATTR_ICON, String.valueOf(subtype.getIconResId()));
out.attribute(null, ATTR_LABEL, String.valueOf(subtype.getNameResId()));
out.attribute(null, ATTR_IME_SUBTYPE_LOCALE, subtype.getLocale());
@@ -3820,7 +3814,7 @@
parser.getAttributeValue(null, ATTR_IS_AUXILIARY)));
final boolean isAsciiCapable = "1".equals(String.valueOf(
parser.getAttributeValue(null, ATTR_IS_ASCII_CAPABLE)));
- final InputMethodSubtype subtype = new InputMethodSubtypeBuilder()
+ final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder()
.setSubtypeNameResId(label)
.setSubtypeIconResId(icon)
.setSubtypeLocale(imeSubtypeLocale)
@@ -3828,9 +3822,13 @@
.setSubtypeMode(imeSubtypeMode)
.setSubtypeExtraValue(imeSubtypeExtraValue)
.setIsAuxiliary(isAuxiliary)
- .setIsAsciiCapable(isAsciiCapable)
- .build();
- tempSubtypesArray.add(subtype);
+ .setIsAsciiCapable(isAsciiCapable);
+ final String subtypeIdString =
+ parser.getAttributeValue(null, ATTR_IME_SUBTYPE_ID);
+ if (subtypeIdString != null) {
+ builder.setSubtypeId(Integer.valueOf(subtypeIdString));
+ }
+ tempSubtypesArray.add(builder.build());
}
}
} catch (XmlPullParserException | IOException | NumberFormatException e) {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 9884a70..7c48634 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -21,6 +21,7 @@
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.ArrayUtils;
import com.android.server.location.ActivityRecognitionProxy;
import com.android.server.location.FlpHardwareProvider;
import com.android.server.location.FusedProxy;
@@ -53,7 +54,6 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
-import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.location.ActivityRecognitionHardware;
@@ -359,12 +359,9 @@
* @param currentUserId the current user, who might have an alter-ego.
*/
void updateUserProfiles(int currentUserId) {
- List<UserInfo> profiles = mUserManager.getProfiles(currentUserId);
+ int[] profileIds = mUserManager.getProfileIdsWithDisabled(currentUserId);
synchronized (mLock) {
- mCurrentUserProfiles = new int[profiles.size()];
- for (int i = 0; i < mCurrentUserProfiles.length; i++) {
- mCurrentUserProfiles[i] = profiles.get(i).id;
- }
+ mCurrentUserProfiles = profileIds;
}
}
@@ -374,12 +371,7 @@
*/
private boolean isCurrentProfile(int userId) {
synchronized (mLock) {
- for (int i = 0; i < mCurrentUserProfiles.length; i++) {
- if (mCurrentUserProfiles[i] == userId) {
- return true;
- }
- }
- return false;
+ return ArrayUtils.contains(mCurrentUserProfiles, userId);
}
}
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
index d136f1a..9ab6300 100644
--- a/services/core/java/com/android/server/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/LockSettingsStorage.java
@@ -401,7 +401,8 @@
return getLockCredentialFilePathForUser(userId, BASE_ZERO_LOCK_PATTERN_FILE);
}
- private String getChildProfileLockFile(int userId) {
+ @VisibleForTesting
+ String getChildProfileLockFile(int userId) {
return getLockCredentialFilePathForUser(userId, CHILD_PROFILE_LOCK_FILE);
}
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index fd9a94d..229a3f4 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -1010,7 +1010,7 @@
Configuration config = new Configuration();
config.setLocale(locale);
try {
- ActivityManagerNative.getDefault().updateConfiguration(config);
+ ActivityManagerNative.getDefault().updatePersistentConfiguration(config);
} catch (RemoteException e) {
Slog.e(TAG, "Error setting system locale from mount service", e);
}
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index d6dbad8..6009984 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -110,6 +110,14 @@
}
/**
+ * Like SystemClock.uptimeMillis, except truncated to an int so it will fit in a message arg.
+ * Inaccurate across 49.7 days of uptime, but only used for debugging.
+ */
+ private int uptimeMillisInt() {
+ return (int) SystemClock.uptimeMillis() & Integer.MAX_VALUE;
+ }
+
+ /**
* Yell loudly if someone tries making future {@link #execute(Command)}
* calls while holding a lock on the given object.
*/
@@ -134,7 +142,9 @@
@Override
public boolean handleMessage(Message msg) {
- String event = (String) msg.obj;
+ final String event = (String) msg.obj;
+ final int start = uptimeMillisInt();
+ final int sent = msg.arg1;
try {
if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) {
log(String.format("Unhandled event '%s'", event));
@@ -145,6 +155,13 @@
if (mCallbacks.onCheckHoldWakeLock(msg.what) && mWakeLock != null) {
mWakeLock.release();
}
+ final int end = uptimeMillisInt();
+ if (start > sent && start - sent > WARN_EXECUTE_DELAY_MS) {
+ loge(String.format("NDC event {%s} processed too late: %dms", event, start - sent));
+ }
+ if (end > start && end - start > WARN_EXECUTE_DELAY_MS) {
+ loge(String.format("NDC event {%s} took too long: %dms", event, end - start));
+ }
}
return true;
}
@@ -214,8 +231,9 @@
mWakeLock.acquire();
releaseWl = true;
}
- if (mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
- event.getCode(), event.getRawEvent()))) {
+ Message msg = mCallbackHandler.obtainMessage(
+ event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());
+ if (mCallbackHandler.sendMessage(msg)) {
releaseWl = false;
}
} else {
@@ -225,7 +243,7 @@
log("Problem parsing message " + e);
} finally {
if (releaseWl) {
- mWakeLock.acquire();
+ mWakeLock.release();
}
}
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index 3eb20a0..801d6e0 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -43,7 +43,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -195,12 +194,8 @@
}
void updateCurrentProfileIds() {
- final List<UserInfo> profiles = mUserManager.getProfiles(mSettings.getCurrentUserId());
- int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
- for (int i = 0; i < currentProfileIds.length; i++) {
- currentProfileIds[i] = profiles.get(i).id;
- }
- mSettings.setCurrentProfileIds(currentProfileIds);
+ mSettings.setCurrentProfileIds(
+ mUserManager.getProfileIdsWithDisabled(mSettings.getCurrentUserId()));
}
private class TextServicesMonitor extends PackageMonitor {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 810270d..4791818 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -85,10 +85,14 @@
import android.util.SparseBooleanArray;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
@@ -125,9 +129,34 @@
public class AccountManagerService
extends IAccountManager.Stub
implements RegisteredServicesCacheListener<AuthenticatorDescription> {
-
private static final String TAG = "AccountManagerService";
+ public static class Lifecycle extends SystemService {
+ private AccountManagerService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mService = new AccountManagerService(getContext());
+ publishBinderService(Context.ACCOUNT_SERVICE, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ mService.systemReady();
+ }
+ }
+
+ @Override
+ public void onUnlockUser(int userHandle) {
+ mService.onUnlockUser(userHandle);
+ }
+ }
+
private static final String DATABASE_NAME = "accounts.db";
private static final int PRE_N_DATABASE_VERSION = 9;
private static final int CE_DATABASE_VERSION = 10;
@@ -266,10 +295,10 @@
private int debugDbInsertionPoint = -1;
private SQLiteStatement statementForLogging;
- UserAccounts(Context context, int userId) {
+ UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
this.userId = userId;
synchronized (cacheLock) {
- openHelper = DeDatabaseHelper.create(context, userId);
+ openHelper = DeDatabaseHelper.create(context, userId, preNDbFile, deDbFile);
}
}
}
@@ -338,15 +367,12 @@
IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_REMOVED);
- userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_USER_REMOVED.equals(action)) {
onUserRemoved(intent);
- } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
- onUserUnlocked(intent);
}
}
}, UserHandle.ALL, userFilter, null, null);
@@ -539,7 +565,9 @@
UserAccounts accounts = mUsers.get(userId);
boolean validateAccounts = false;
if (accounts == null) {
- accounts = new UserAccounts(mContext, userId);
+ File preNDbFile = new File(getPreNDatabaseName(userId));
+ File deDbFile = new File(getDeDatabaseName(userId));
+ accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
initializeDebugDbSizeAndCompileSqlStatementForLogging(
accounts.openHelper.getWritableDatabase(), accounts);
mUsers.append(userId, accounts);
@@ -550,10 +578,12 @@
if (!accounts.openHelper.isCeDatabaseAttached() && mUnlockedUsers.get(userId)) {
Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
synchronized (accounts.cacheLock) {
- CeDatabaseHelper.create(mContext, userId);
- accounts.openHelper.attachCeDatabase();
+ File preNDatabaseFile = new File(getPreNDatabaseName(userId));
+ File ceDatabaseFile = new File(getCeDatabaseName(userId));
+ CeDatabaseHelper.create(mContext, userId, preNDatabaseFile, ceDatabaseFile);
+ accounts.openHelper.attachCeDatabase(ceDatabaseFile);
}
- // TODO Synchronize accounts by removing CE account not available in DE
+ syncDeCeAccountsLocked(accounts);
}
if (validateAccounts) {
validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
@@ -562,6 +592,21 @@
}
}
+ private void syncDeCeAccountsLocked(UserAccounts accounts) {
+ Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
+ final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
+ List<Account> accountsToRemove = CeDatabaseHelper.findCeAccountsNotInDe(db);
+ if (!accountsToRemove.isEmpty()) {
+ Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user "
+ + accounts.userId + " was locked. Removing accounts from CE tables");
+ logRecord(accounts, DebugDbHelper.ACTION_SYNC_DE_CE_ACCOUNTS, TABLE_ACCOUNTS);
+
+ for (Account account : accountsToRemove) {
+ removeAccountInternal(accounts, account, Process.myUid());
+ }
+ }
+ }
+
private void purgeOldGrantsAll() {
synchronized (mUsers) {
for (int i = 0; i < mUsers.size(); i++) {
@@ -631,8 +676,12 @@
}
}
- private void onUserUnlocked(Intent intent) {
- int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ @VisibleForTesting
+ void onUserUnlocked(Intent intent) {
+ onUnlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
+ }
+
+ void onUnlockUser(int userId) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "onUserUnlocked " + userId);
}
@@ -799,7 +848,7 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "getAuthenticatorTypes: "
+ "for user id " + userId
- + "caller's uid " + callingUid
+ + " caller's uid " + callingUid
+ ", pid " + Binder.getCallingPid());
}
// Only allow the system process to read accounts of other users
@@ -1537,27 +1586,41 @@
}
}
- /* For testing */
+ @VisibleForTesting
protected void removeAccountInternal(Account account) {
removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
}
private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
- // For now user is required to be unlocked. TODO: Handle both cases in the future
int deleted;
+ boolean userUnlocked = isUserUnlocked(accounts.userId);
+ if (!userUnlocked) {
+ Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId
+ + " is still locked. CE data will be removed later");
+ }
synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
+ final SQLiteDatabase db = userUnlocked
+ ? accounts.openHelper.getWritableDatabaseUserIsUnlocked()
+ : accounts.openHelper.getWritableDatabase();
final long accountId = getAccountIdLocked(db, account);
- deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
- + "=?",
- new String[]{account.name, account.type});
- // Delete from CE table
- db.delete(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
- new String[]{account.name, account.type});
+ db.beginTransaction();
+ try {
+ deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
+ + "=?", new String[]{account.name, account.type});
+ if (userUnlocked) {
+ // Delete from CE table
+ deleted = db.delete(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
+ + "=?", new String[]{account.name, account.type});
+ }
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
removeAccountFromCacheLocked(accounts, account);
sendAccountsChangedBroadcast(accounts.userId);
-
- logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_ACCOUNTS, accountId, accounts);
+ String action = userUnlocked ? DebugDbHelper.ACTION_ACCOUNT_REMOVE
+ : DebugDbHelper.ACTION_ACCOUNT_REMOVE_DE;
+ logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts);
}
long id = Binder.clearCallingIdentity();
try {
@@ -4014,7 +4077,8 @@
}
}
- static String getPreNDatabaseName(int userId) {
+ @VisibleForTesting
+ String getPreNDatabaseName(int userId) {
File systemDir = Environment.getDataSystemDirectory();
File databaseFile = new File(Environment.getUserSystemDirectory(userId),
PRE_N_DATABASE_NAME);
@@ -4040,13 +4104,15 @@
return databaseFile.getPath();
}
- static String getDeDatabaseName(int userId) {
+ @VisibleForTesting
+ String getDeDatabaseName(int userId) {
File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
DE_DATABASE_NAME);
return databaseFile.getPath();
}
- static String getCeDatabaseName(int userId) {
+ @VisibleForTesting
+ String getCeDatabaseName(int userId) {
File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
CE_DATABASE_NAME);
return databaseFile.getPath();
@@ -4072,6 +4138,7 @@
private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
private static String ACTION_ACCOUNT_ADD = "action_account_add";
private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
+ private static String ACTION_ACCOUNT_REMOVE_DE = "action_account_remove_de";
private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
@@ -4082,6 +4149,7 @@
// who called.
private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
+ private static String ACTION_SYNC_DE_CE_ACCOUNTS = "action_sync_de_ce_accounts";
//This action doesn't add account to accountdb. Account is only
// added in finishSession which may be in a different user profile.
@@ -4185,13 +4253,11 @@
}
static class PreNDatabaseHelper extends SQLiteOpenHelper {
-
private final Context mContext;
private final int mUserId;
- public PreNDatabaseHelper(Context context, int userId) {
- super(context, AccountManagerService.getPreNDatabaseName(userId), null,
- PRE_N_DATABASE_VERSION);
+ public PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) {
+ super(context, preNDatabaseName, null, PRE_N_DATABASE_VERSION);
mContext = context;
mUserId = userId;
}
@@ -4328,8 +4394,8 @@
private final int mUserId;
private volatile boolean mCeAttached;
- private DeDatabaseHelper(Context context, int userId) {
- super(context, getDeDatabaseName(userId), null, DE_DATABASE_VERSION);
+ private DeDatabaseHelper(Context context, int userId, String deDatabaseName) {
+ super(context, deDatabaseName, null, DE_DATABASE_VERSION);
mUserId = userId;
}
@@ -4394,8 +4460,7 @@
}
}
- public void attachCeDatabase() {
- File ceDbFile = new File(getCeDatabaseName(mUserId));
+ public void attachCeDatabase(File ceDbFile) {
SQLiteDatabase db = getWritableDatabase();
db.execSQL("ATTACH DATABASE '" + ceDbFile.getPath()+ "' AS ceDb");
mCeAttached = true;
@@ -4408,8 +4473,8 @@
public SQLiteDatabase getReadableDatabaseUserIsUnlocked() {
if(!mCeAttached) {
- Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user "
- + mUserId + " is still locked ", new Throwable());
+ Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user " + mUserId
+ + " is still locked. CE database is not yet available.", new Throwable());
}
return super.getReadableDatabase();
}
@@ -4417,7 +4482,7 @@
public SQLiteDatabase getWritableDatabaseUserIsUnlocked() {
if(!mCeAttached) {
Log.wtf(TAG, "getWritableDatabaseUserIsUnlocked called while user " + mUserId
- + " is still locked ", new Throwable());
+ + " is still locked. CE database is not yet available.", new Throwable());
}
return super.getWritableDatabase();
}
@@ -4470,20 +4535,24 @@
db.execSQL("DETACH DATABASE preNDb");
}
- static DeDatabaseHelper create(Context context, int userId) {
- File oldDb = new File(getPreNDatabaseName(userId));
- File newDb = new File(getDeDatabaseName(userId));
- boolean newDbExists = newDb.exists();
- DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId);
+ static DeDatabaseHelper create(
+ Context context,
+ int userId,
+ File preNDatabaseFile,
+ File deDatabaseFile) {
+ boolean newDbExists = deDatabaseFile.exists();
+ DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId,
+ deDatabaseFile.getPath());
// If the db just created, and there is a legacy db, migrate it
- if (!newDbExists && oldDb.exists()) {
+ if (!newDbExists && preNDatabaseFile.exists()) {
// Migrate legacy db to the latest version - PRE_N_DATABASE_VERSION
- PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId);
+ PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId,
+ preNDatabaseFile.getPath());
// Open the database to force upgrade if required
preNDatabaseHelper.getWritableDatabase();
preNDatabaseHelper.close();
// Move data without SPII to DE
- deDatabaseHelper.migratePreNDbToDe(oldDb);
+ deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile);
}
return deDatabaseHelper;
}
@@ -4491,8 +4560,8 @@
static class CeDatabaseHelper extends SQLiteOpenHelper {
- public CeDatabaseHelper(Context context, int userId) {
- super(context, getCeDatabaseName(userId), null, CE_DATABASE_VERSION);
+ public CeDatabaseHelper(Context context, String ceDatabaseName) {
+ super(context, ceDatabaseName, null, CE_DATABASE_VERSION);
}
/**
@@ -4580,33 +4649,56 @@
}
}
+ static List<Account> findCeAccountsNotInDe(SQLiteDatabase db) {
+ // Select accounts from CE that do not exist in DE
+ Cursor cursor = db.rawQuery(
+ "SELECT " + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
+ + " FROM " + CE_TABLE_ACCOUNTS
+ + " WHERE NOT EXISTS "
+ + " (SELECT " + ACCOUNTS_ID + " FROM " + TABLE_ACCOUNTS
+ + " WHERE " + ACCOUNTS_ID + "=" + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
+ + " )", null);
+ try {
+ List<Account> accounts = new ArrayList<>(cursor.getCount());
+ while (cursor.moveToNext()) {
+ String accountName = cursor.getString(0);
+ String accountType = cursor.getString(1);
+ accounts.add(new Account(accountName, accountType));
+ }
+ return accounts;
+ } finally {
+ cursor.close();
+ }
+ }
+
/**
* Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location,
* it also performs migration to the new CE database.
* @param context
* @param userId id of the user where the database is located
*/
- static CeDatabaseHelper create(Context context, int userId) {
-
- File oldDatabaseFile = new File(getPreNDatabaseName(userId));
- File ceDatabaseFile = new File(getCeDatabaseName(userId));
+ static CeDatabaseHelper create(
+ Context context,
+ int userId,
+ File preNDatabaseFile,
+ File ceDatabaseFile) {
boolean newDbExists = ceDatabaseFile.exists();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists="
- + oldDatabaseFile.exists() + " newDbExists=" + newDbExists);
+ + preNDatabaseFile.exists() + " newDbExists=" + newDbExists);
}
boolean removeOldDb = false;
- if (!newDbExists && oldDatabaseFile.exists()) {
- removeOldDb = migratePreNDbToCe(oldDatabaseFile, ceDatabaseFile);
+ if (!newDbExists && preNDatabaseFile.exists()) {
+ removeOldDb = migratePreNDbToCe(preNDatabaseFile, ceDatabaseFile);
}
// Try to open and upgrade if necessary
- CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, userId);
+ CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, ceDatabaseFile.getPath());
ceHelper.getWritableDatabase();
ceHelper.close();
if (removeOldDb) {
// TODO STOPSHIP - backup file during testing. Remove file before the release
- Log.i(TAG, "Migration complete - creating backup of old db " + oldDatabaseFile);
- renameToBakFile(oldDatabaseFile);
+ Log.i(TAG, "Migration complete - creating backup of old db " + preNDatabaseFile);
+ renameToBakFile(preNDatabaseFile);
}
return ceHelper;
}
@@ -4771,12 +4863,14 @@
}
}
+ @VisibleForTesting
protected void installNotification(final int notificationId, final Notification n,
UserHandle user) {
((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
.notifyAsUser(null, notificationId, n, user);
}
+ @VisibleForTesting
protected void cancelNotification(int id, UserHandle user) {
long identityToken = clearCallingIdentity();
try {
@@ -5314,7 +5408,7 @@
HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
if (userDataForAccount == null) {
// need to populate the cache for this account
- final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
accounts.userDataCache.put(account, userDataForAccount);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0f48d21..c01b4f5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8792,6 +8792,7 @@
rti.bounds = new Rect(tr.mBounds);
}
rti.isDockable = tr.canGoInDockedStack();
+ rti.resizeMode = tr.mResizeMode;
ActivityRecord base = null;
ActivityRecord top = null;
@@ -13164,6 +13165,9 @@
}
}
+ private volatile long mWtfClusterStart;
+ private volatile int mWtfClusterCount;
+
/**
* Write a description of an error (crash, WTF, ANR) to the drop box.
* @param eventType to include in the drop box tag ("crash", "wtf", etc.)
@@ -13190,6 +13194,16 @@
// Exit early if the dropbox isn't configured to accept this report type.
if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
+ // Rate-limit how often we're willing to do the heavy lifting below to
+ // collect and record logs; currently 5 logs per 10 second period.
+ final long now = SystemClock.elapsedRealtime();
+ if (now - mWtfClusterStart > 10 * DateUtils.SECOND_IN_MILLIS) {
+ mWtfClusterStart = now;
+ mWtfClusterCount = 1;
+ } else {
+ if (mWtfClusterCount++ >= 5) return;
+ }
+
final StringBuilder sb = new StringBuilder(1024);
appendDropBoxProcessHeaders(process, processName, sb);
if (process != null) {
@@ -17131,7 +17145,7 @@
// but historically it has not been protected and apps may be using it
// to poke their own app widget. So, instead of making it protected,
// just limit it to the caller.
- if (callerApp == null) {
+ if (callerPackage == null) {
String msg = "Permission Denial: not allowed to send broadcast "
+ action + " from unknown caller.";
Slog.w(TAG, msg);
@@ -17140,17 +17154,17 @@
// They are good enough to send to an explicit component... verify
// it is being sent to the calling app.
if (!intent.getComponent().getPackageName().equals(
- callerApp.info.packageName)) {
+ callerPackage)) {
String msg = "Permission Denial: not allowed to send broadcast "
+ action + " to "
+ intent.getComponent().getPackageName() + " from "
- + callerApp.info.packageName;
+ + callerPackage;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
} else {
// Limit broadcast to their own package.
- intent.setPackage(callerApp.info.packageName);
+ intent.setPackage(callerPackage);
}
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 2e9947a..837a1c1 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -4776,6 +4776,7 @@
ci.numActivities = numActivities;
ci.numRunning = numRunning;
ci.isDockable = task.canGoInDockedStack();
+ ci.resizeMode = task.mResizeMode;
list.add(ci);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index d34e8fc..53c6024 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -4248,6 +4248,7 @@
// Work Challenge is present) let startActivityInPackage handle the intercepting.
if (!mService.mUserController.shouldConfirmCredentials(task.userId)
&& task.getRootActivity() != null) {
+ mActivityMetricsLogger.notifyActivityLaunching();
mService.moveTaskToFrontLocked(task.taskId, 0, bOptions);
// If we are launching the task in the docked stack, put it into resizing mode so
diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java
index 1825c88..0e192ea 100644
--- a/services/core/java/com/android/server/am/PreBootBroadcaster.java
+++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java
@@ -69,6 +69,12 @@
return;
}
+ if (!mService.isUserRunning(mUserId, 0)) {
+ Slog.i(TAG, "User " + mUserId + " is no longer running; skipping remaining receivers");
+ onFinished();
+ return;
+ }
+
final ResolveInfo ri = mTargets.get(mIndex++);
final ComponentName componentName = ri.activityInfo.getComponentName();
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index bea26c7..5ebb9a7 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -278,6 +278,31 @@
// Dispatch unlocked to system services
mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0));
+ // Dispatch unlocked to external apps
+ final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
+ unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ unlockedIntent.addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
+ mService.broadcastIntentLocked(null, null, unlockedIntent, null, null, 0, null,
+ null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ userId);
+
+ if (getUserInfo(userId).isManagedProfile()) {
+ UserInfo parent = getUserManager().getProfileParent(userId);
+ if (parent != null) {
+ final Intent profileUnlockedIntent = new Intent(
+ Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
+ profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
+ profileUnlockedIntent.addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ mService.broadcastIntentLocked(null, null, profileUnlockedIntent,
+ null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, false, false, MY_PID, SYSTEM_UID,
+ parent.id);
+ }
+ }
+
// Send PRE_BOOT broadcasts if fingerprint changed
final UserInfo info = getUserInfo(userId);
if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) {
@@ -320,31 +345,6 @@
// Remember that we logged in
mUserManager.onUserLoggedIn(userId);
- // Dispatch unlocked to external apps
- final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
- unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- unlockedIntent.addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
- mService.broadcastIntentLocked(null, null, unlockedIntent, null, null, 0, null,
- null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
- userId);
-
- if (getUserInfo(userId).isManagedProfile()) {
- UserInfo parent = getUserManager().getProfileParent(userId);
- if (parent != null) {
- final Intent profileUnlockedIntent = new Intent(
- Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
- profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
- profileUnlockedIntent.addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- mService.broadcastIntentLocked(null, null, profileUnlockedIntent,
- null, null, 0, null, null, null, AppOpsManager.OP_NONE,
- null, false, false, MY_PID, SYSTEM_UID,
- parent.id);
- }
- }
-
final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
@@ -893,8 +893,9 @@
boolean unlockUserCleared(final int userId, byte[] token, byte[] secret,
ProgressReporter progress) {
synchronized (mService) {
- // Bail if already running unlocked
+ // Bail if already running unlocked, or if not running at all
final UserState uss = mStartedUsers.get(userId);
+ if (uss == null) return false;
switch (uss.state) {
case STATE_RUNNING_UNLOCKING:
case STATE_RUNNING_UNLOCKED:
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 58db985..a6dfab0 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1290,7 +1290,7 @@
// Check if the ringer mode handles this adjustment. If it does we don't
// need to adjust the volume further.
final int result = checkForRingerModeChange(aliasIndex, direction, step,
- streamState.mIsMuted);
+ streamState.mIsMuted, callingPackage, flags);
adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;
// If suppressing a volume adjustment in silent mode, display the UI hint
if ((result & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
@@ -1302,8 +1302,7 @@
}
}
// If the ringermode is suppressing media, prevent changes
- if (streamTypeAlias == AudioSystem.STREAM_MUSIC
- && (mRingerModeMutedStreams & (1 << AudioSystem.STREAM_MUSIC)) != 0) {
+ if (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) {
adjustVolume = false;
}
int oldIndex = mStreamStates[streamType].getIndex(device);
@@ -1551,6 +1550,10 @@
throw new SecurityException("Not allowed to change Do Not Disturb state");
}
+ if (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) {
+ return;
+ }
+
synchronized (mSafeMediaVolumeState) {
// reset any pending volume command
mPendingVolumeCommand = null;
@@ -1601,6 +1604,19 @@
sendVolumeUpdate(streamType, oldIndex, index, flags);
}
+ // No ringer affected streams can be changed in total silence mode except those that
+ // will cause the device to exit total silence mode.
+ private boolean volumeAdjustmentAllowedByDnd(int streamTypeAlias, int flags) {
+ if (mNm.getZenMode() == Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
+ && isStreamMutedByRingerMode(streamTypeAlias)) {
+ if (!(((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
+ (streamTypeAlias == getUiSoundsStreamType()))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
/** @see AudioManager#forceVolumeControlStream(int) */
public void forceVolumeControlStream(int streamType, IBinder cb) {
synchronized(mForceControlStreamLock) {
@@ -3366,7 +3382,8 @@
* adjusting volume. If so, this will set the proper ringer mode and volume
* indices on the stream states.
*/
- private int checkForRingerModeChange(int oldIndex, int direction, int step, boolean isMuted) {
+ private int checkForRingerModeChange(int oldIndex, int direction, int step, boolean isMuted,
+ String caller, int flags) {
final boolean isTv = mPlatformType == AudioSystem.PLATFORM_TELEVISION;
int result = FLAG_ADJUST_VOLUME;
int ringerMode = getRingerModeInternal();
@@ -3455,6 +3472,12 @@
break;
}
+ if (isAndroidNPlus(caller) && wouldToggleZenMode(ringerMode)
+ && !mNm.isNotificationPolicyAccessGrantedForPackage(caller)
+ && (flags & AudioManager.FLAG_FROM_KEY) == 0) {
+ throw new SecurityException("Not allowed to change Do Not Disturb state");
+ }
+
setRingerMode(ringerMode, TAG + ".checkForRingerModeChange", false /*external*/);
mPrevVolDirection = direction;
diff --git a/services/core/java/com/android/server/camera/CameraService.java b/services/core/java/com/android/server/camera/CameraService.java
index cd8eb4e..7d9adf2 100644
--- a/services/core/java/com/android/server/camera/CameraService.java
+++ b/services/core/java/com/android/server/camera/CameraService.java
@@ -15,32 +15,28 @@
*/
package com.android.server.camera;
-import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.UserInfo;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceProxy;
import android.nfc.INfcAdapter;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Binder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
-import android.os.UserManager;
import android.os.SystemProperties;
-import android.util.Slog;
+import android.os.UserManager;
import android.util.ArraySet;
+import android.util.Slog;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
import java.util.Set;
/**
@@ -225,11 +221,11 @@
}
private Set<Integer> getEnabledUserHandles(int currentUserHandle) {
- List<UserInfo> userProfiles = mUserManager.getEnabledProfiles(currentUserHandle);
- Set<Integer> handles = new HashSet<>(userProfiles.size());
+ int[] userProfiles = mUserManager.getEnabledProfileIds(currentUserHandle);
+ Set<Integer> handles = new ArraySet<>(userProfiles.length);
- for (UserInfo i : userProfiles) {
- handles.add(i.id);
+ for (int id : userProfiles) {
+ handles.add(id);
}
return handles;
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index bbb162e..d330756 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -120,8 +120,9 @@
/**
* Inform ConnectivityService that the network has been tested.
- * obj = NetworkAgentInfo
+ * obj = String representing URL that Internet probe was redirect to, if it was redirected.
* arg1 = One of the NETWORK_TESTED_RESULT_* constants.
+ * arg2 = NetID.
*/
public static final int EVENT_NETWORK_TESTED = BASE + 2;
@@ -334,8 +335,8 @@
mDontDisplaySigninNotification = true;
mUserDoesNotWant = true;
mConnectivityServiceHandler.sendMessage(obtainMessage(
- EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, 0,
- mNetworkAgentInfo));
+ EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID,
+ mNetworkAgentInfo.network.netId, null));
// TODO: Should teardown network.
mUidResponsibleForReeval = 0;
transitionTo(mEvaluatingState);
@@ -358,7 +359,7 @@
CaptivePortalStateChangeEvent.logEvent(
CaptivePortalStateChangeEvent.NETWORK_MONITOR_VALIDATED);
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
- NETWORK_TEST_RESULT_VALID, 0, mNetworkAgentInfo));
+ NETWORK_TEST_RESULT_VALID, mNetworkAgentInfo.network.netId, null));
}
@Override
@@ -412,6 +413,21 @@
}
}
+ /**
+ * Result of calling isCaptivePortal().
+ * @hide
+ */
+ @VisibleForTesting
+ public static final class CaptivePortalProbeResult {
+ final int mHttpResponseCode; // HTTP response code returned from Internet probe.
+ final String mRedirectUrl; // Redirect destination returned from Internet probe.
+
+ public CaptivePortalProbeResult(int httpResponseCode, String redirectUrl) {
+ mHttpResponseCode = httpResponseCode;
+ mRedirectUrl = redirectUrl;
+ }
+ }
+
// Being in the EvaluatingState State indicates the Network is being evaluated for internet
// connectivity, or that the user has indicated that this network is unwanted.
private class EvaluatingState extends State {
@@ -464,19 +480,23 @@
// IPv6) could each take SOCKET_TIMEOUT_MS. During this time this StateMachine
// will be unresponsive. isCaptivePortal() could be executed on another Thread
// if this is found to cause problems.
- int httpResponseCode = isCaptivePortal();
+ CaptivePortalProbeResult probeResult = isCaptivePortal();
CaptivePortalCheckResultEvent.logEvent(mNetworkAgentInfo.network.netId,
- httpResponseCode);
- if (httpResponseCode == 204) {
+ probeResult.mHttpResponseCode);
+ if (probeResult.mHttpResponseCode == 204) {
transitionTo(mValidatedState);
- } else if (httpResponseCode >= 200 && httpResponseCode <= 399) {
+ } else if (probeResult.mHttpResponseCode >= 200 &&
+ probeResult.mHttpResponseCode <= 399) {
+ mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
+ NETWORK_TEST_RESULT_INVALID, mNetworkAgentInfo.network.netId,
+ probeResult.mRedirectUrl));
transitionTo(mCaptivePortalState);
} else {
final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
sendMessageDelayed(msg, mReevaluateDelayMs);
mConnectivityServiceHandler.sendMessage(obtainMessage(
- EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, 0,
- mNetworkAgentInfo));
+ EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID,
+ mNetworkAgentInfo.network.netId, probeResult.mRedirectUrl));
if (mAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
// Don't continue to blame UID forever.
TrafficStats.clearThreadStatsUid();
@@ -533,8 +553,6 @@
@Override
public void enter() {
- mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
- NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
// Don't annoy user with sign-in notifications.
if (mDontDisplaySigninNotification) return;
// Create a CustomIntentReceiver that sends us a
@@ -639,11 +657,12 @@
* Returns HTTP response code.
*/
@VisibleForTesting
- protected int isCaptivePortal() {
- if (!mIsCaptivePortalCheckEnabled) return 204;
+ protected CaptivePortalProbeResult isCaptivePortal() {
+ if (!mIsCaptivePortalCheckEnabled) return new CaptivePortalProbeResult(204, null);
HttpURLConnection urlConnection = null;
int httpResponseCode = 599;
+ String redirectUrl = null;
try {
URL url = new URL(getCaptivePortalServerUrl(mContext));
// On networks with a PAC instead of fetching a URL that should result in a 204
@@ -699,6 +718,7 @@
long requestTimestamp = SystemClock.elapsedRealtime();
httpResponseCode = urlConnection.getResponseCode();
+ redirectUrl = urlConnection.getHeaderField("location");
// Time how long it takes to get a response to our request
long responseTimestamp = SystemClock.elapsedRealtime();
@@ -739,7 +759,7 @@
urlConnection.disconnect();
}
}
- return httpResponseCode;
+ return new CaptivePortalProbeResult(httpResponseCode, redirectUrl);
}
/**
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 2cba93fd..79b5978 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -57,12 +57,16 @@
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.IState;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.MessageUtils;
+import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.IoThread;
@@ -95,6 +99,12 @@
private final static boolean DBG = false;
private final static boolean VDBG = false;
+ private static final Class[] messageClasses = {
+ Tethering.class, TetherMasterSM.class, TetherInterfaceSM.class
+ };
+ private static final SparseArray<String> sMagicDecoderRing =
+ MessageUtils.findMessageNames(messageClasses);
+
// TODO - remove both of these - should be part of interface inspection/selection stuff
private String[] mTetherableUsbRegexs;
private String[] mTetherableWifiRegexs;
@@ -235,6 +245,8 @@
@Override
public void interfaceStatusChanged(String iface, boolean up) {
+ // Never called directly: only called from interfaceLinkStateChanged.
+ // See NetlinkHandler.cpp:71.
if (VDBG) Log.d(TAG, "interfaceStatusChanged " + iface + ", " + up);
boolean found = false;
boolean usb = false;
@@ -274,7 +286,6 @@
@Override
public void interfaceLinkStateChanged(String iface, boolean up) {
- if (VDBG) Log.d(TAG, "interfaceLinkStateChanged " + iface + ", " + up);
interfaceStatusChanged(iface, up);
}
@@ -660,8 +671,11 @@
erroredList);
mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
if (DBG) {
- Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " +
- activeList.size() + ", " + erroredList.size());
+ Log.d(TAG, String.format(
+ "sendTetherStateChangedBroadcast avail=[%s] active=[%s] error=[%s]",
+ TextUtils.join(",", availableList),
+ TextUtils.join(",", activeList),
+ TextUtils.join(",", erroredList)));
}
if (usbTethered) {
@@ -989,31 +1003,39 @@
return retVal;
}
+ private void maybeLogMessage(State state, int what) {
+ if (DBG) {
+ Log.d(TAG, state.getName() + " got " +
+ sMagicDecoderRing.get(what, Integer.toString(what)));
+ }
+ }
+
class TetherInterfaceSM extends StateMachine {
+ private static final int BASE_IFACE = Protocol.BASE_TETHERING + 100;
// notification from the master SM that it's not in tether mode
- static final int CMD_TETHER_MODE_DEAD = 1;
+ static final int CMD_TETHER_MODE_DEAD = BASE_IFACE + 1;
// request from the user that it wants to tether
- static final int CMD_TETHER_REQUESTED = 2;
+ static final int CMD_TETHER_REQUESTED = BASE_IFACE + 2;
// request from the user that it wants to untether
- static final int CMD_TETHER_UNREQUESTED = 3;
+ static final int CMD_TETHER_UNREQUESTED = BASE_IFACE + 3;
// notification that this interface is down
- static final int CMD_INTERFACE_DOWN = 4;
+ static final int CMD_INTERFACE_DOWN = BASE_IFACE + 4;
// notification that this interface is up
- static final int CMD_INTERFACE_UP = 5;
+ static final int CMD_INTERFACE_UP = BASE_IFACE + 5;
// notification from the master SM that it had an error turning on cellular dun
- static final int CMD_CELL_DUN_ERROR = 6;
+ static final int CMD_CELL_DUN_ERROR = BASE_IFACE + 6;
// notification from the master SM that it had trouble enabling IP Forwarding
- static final int CMD_IP_FORWARDING_ENABLE_ERROR = 7;
+ static final int CMD_IP_FORWARDING_ENABLE_ERROR = BASE_IFACE + 7;
// notification from the master SM that it had trouble disabling IP Forwarding
- static final int CMD_IP_FORWARDING_DISABLE_ERROR = 8;
+ static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8;
// notification from the master SM that it had trouble starting tethering
- static final int CMD_START_TETHERING_ERROR = 9;
+ static final int CMD_START_TETHERING_ERROR = BASE_IFACE + 9;
// notification from the master SM that it had trouble stopping tethering
- static final int CMD_STOP_TETHERING_ERROR = 10;
+ static final int CMD_STOP_TETHERING_ERROR = BASE_IFACE + 10;
// notification from the master SM that it had trouble setting the DNS forwarders
- static final int CMD_SET_DNS_FORWARDERS_ERROR = 11;
+ static final int CMD_SET_DNS_FORWARDERS_ERROR = BASE_IFACE + 11;
// the upstream connection has changed
- static final int CMD_TETHER_CONNECTION_CHANGED = 12;
+ static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IFACE + 12;
private State mDefaultState;
@@ -1124,7 +1146,7 @@
@Override
public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, "InitialState.processMessage what=" + message.what);
+ maybeLogMessage(this, message.what);
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_REQUESTED:
@@ -1165,7 +1187,7 @@
}
@Override
public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, "StartingState.processMessage what=" + message.what);
+ maybeLogMessage(this, message.what);
boolean retValue = true;
switch (message.what) {
// maybe a parent class?
@@ -1255,7 +1277,7 @@
@Override
public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, "TetheredState.processMessage what=" + message.what);
+ maybeLogMessage(this, message.what);
boolean retValue = true;
boolean error = false;
switch (message.what) {
@@ -1480,18 +1502,19 @@
}
class TetherMasterSM extends StateMachine {
+ private static final int BASE_MASTER = Protocol.BASE_TETHERING;
// an interface SM has requested Tethering
- static final int CMD_TETHER_MODE_REQUESTED = 1;
+ static final int CMD_TETHER_MODE_REQUESTED = BASE_MASTER + 1;
// an interface SM has unrequested Tethering
- static final int CMD_TETHER_MODE_UNREQUESTED = 2;
+ static final int CMD_TETHER_MODE_UNREQUESTED = BASE_MASTER + 2;
// upstream connection change - do the right thing
- static final int CMD_UPSTREAM_CHANGED = 3;
+ static final int CMD_UPSTREAM_CHANGED = BASE_MASTER + 3;
// we don't have a valid upstream conn, check again after a delay
- static final int CMD_RETRY_UPSTREAM = 4;
+ static final int CMD_RETRY_UPSTREAM = BASE_MASTER + 4;
// Events from NetworkCallbacks that we process on the master state
// machine thread on behalf of the UpstreamNetworkMonitor.
- static final int EVENT_UPSTREAM_LINKPROPERTIES_CHANGED = 5;
- static final int EVENT_UPSTREAM_LOST = 6;
+ static final int EVENT_UPSTREAM_LINKPROPERTIES_CHANGED = BASE_MASTER + 5;
+ static final int EVENT_UPSTREAM_LOST = BASE_MASTER + 6;
// This indicates what a timeout event relates to. A state that
// sends itself a delayed timeout event and handles incoming timeout events
@@ -1748,7 +1771,7 @@
}
protected void notifyTetheredOfNewUpstreamIface(String ifaceName) {
- if (DBG) Log.d(TAG, "notifying tethered with iface =" + ifaceName);
+ if (DBG) Log.d(TAG, "Notifying tethered with upstream=" + ifaceName);
mCurrentUpstreamIface = ifaceName;
for (TetherInterfaceSM sm : mNotifyList) {
sm.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED,
@@ -1862,7 +1885,7 @@
}
@Override
public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, "MasterInitialState.processMessage what=" + message.what);
+ maybeLogMessage(this, message.what);
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
@@ -1910,7 +1933,7 @@
}
@Override
public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, "TetherModeAliveState.processMessage what=" + message.what);
+ maybeLogMessage(this, message.what);
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 6e7ea99..03eb019 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -77,7 +77,7 @@
static final boolean DEBUG = false;
public static class Lifecycle extends SystemService {
- private ContentService mContentService;
+ private ContentService mService;
public Lifecycle(Context context) {
super(context);
@@ -87,14 +87,21 @@
public void onStart() {
final boolean factoryTest = (FactoryTest
.getMode() == FactoryTest.FACTORY_TEST_LOW_LEVEL);
- mContentService = new ContentService(getContext(), factoryTest);
- publishBinderService(ContentResolver.CONTENT_SERVICE_NAME, mContentService);
+ mService = new ContentService(getContext(), factoryTest);
+ publishBinderService(ContentResolver.CONTENT_SERVICE_NAME, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ mService.systemReady();
+ }
}
@Override
public void onCleanupUser(int userHandle) {
- synchronized (mContentService.mCache) {
- mContentService.mCache.remove(userHandle);
+ synchronized (mService.mCache) {
+ mService.mCache.remove(userHandle);
}
}
}
@@ -265,7 +272,7 @@
localeFilter, null, null);
}
- public void systemReady() {
+ void systemReady() {
getSyncManager();
}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 3d8bf51..e3f3849 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -541,10 +541,8 @@
UserManager um = UserManager.get(mContext);
// Allow current user or profiles of the current user...
- List<UserInfo> profiles = um.getEnabledProfiles(userId);
- final int n = profiles.size();
- for (int i = 0; i < n; i++) {
- if (profiles.get(i).id == userId) {
+ for (int profileId : um.getEnabledProfileIds(userId)) {
+ if (profileId == userId) {
return true;
}
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index d80dc3b..6b916be 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -40,7 +40,6 @@
import android.location.IGnssStatusProvider;
import android.location.GnssMeasurementsEvent;
import android.location.GnssNavigationMessage;
-import android.location.GnssNavigationMessageEvent;
import android.location.IGpsGeofenceHardware;
import android.location.ILocationManager;
import android.location.INetInitiatedListener;
@@ -1668,16 +1667,6 @@
}
/**
- * called from native code - GPS navigation message callback
- */
- private void reportNavigationMessage(GnssNavigationMessageEvent event) {
- if (event != null) {
- mGnssNavigationMessageProvider
- .onNavigationMessageAvailable(event.getNavigationMessage());
- }
- }
-
- /**
* called from native code to inform us what the GPS engine capabilities are
*/
private void setEngineCapabilities(int capabilities) {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 6d9fed4..c31d93e 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -97,8 +97,6 @@
// List of packages in restored setting across all mUserProfiles, for quick
// filtering upon package updates.
private ArraySet<String> mRestoredPackages = new ArraySet<>();
- // State of current service categories
- private ArrayMap<String, Boolean> mCategoryEnabled = new ArrayMap<>();
// List of enabled packages that have nevertheless asked not to be run
private ArraySet<ComponentName> mSnoozingForCurrentProfiles = new ArraySet<>();
@@ -342,47 +340,6 @@
}
}
- public void setCategoryState(String category, boolean enabled) {
- synchronized (mMutex) {
- final Boolean previous = mCategoryEnabled.put(category, enabled);
- if (!(previous == null || previous != enabled)) {
- return;
- }
-
- // State changed
- if (DEBUG) {
- Slog.d(TAG, ((enabled) ? "Enabling " : "Disabling ") + "category " + category);
- }
-
- final int[] userIds = mUserProfiles.getCurrentProfileIds();
- for (int userId : userIds) {
- final Set<ComponentName> componentNames = queryPackageForServices(null,
- userId, category);
-
- // Disallow services not enabled in Settings
- final ArraySet<ComponentName> userComponents =
- loadComponentNamesFromSetting(mConfig.secureSettingName, userId);
- if (userComponents == null) {
- componentNames.clear();
- } else {
- componentNames.retainAll(userComponents);
- }
-
- if (DEBUG) {
- Slog.d(TAG, "Components for category " + category + ": " + componentNames);
- }
- for (ComponentName c : componentNames) {
- if (enabled) {
- registerServiceLocked(c, userId);
- } else {
- unregisterServiceLocked(c, userId);
- }
- }
- }
-
- }
- }
-
private void rebuildRestoredPackages() {
mRestoredPackages.clear();
mSnoozingForCurrentProfiles.clear();
@@ -454,20 +411,12 @@
}
protected Set<ComponentName> queryPackageForServices(String packageName, int userId) {
- return queryPackageForServices(packageName, userId, null);
- }
-
- public Set<ComponentName> queryPackageForServices(String packageName, int userId,
- String category) {
Set<ComponentName> installed = new ArraySet<>();
final PackageManager pm = mContext.getPackageManager();
Intent queryIntent = new Intent(mConfig.serviceInterface);
if (!TextUtils.isEmpty(packageName)) {
queryIntent.setPackage(packageName);
}
- if (category != null) {
- queryIntent.addCategory(category);
- }
List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
queryIntent,
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
@@ -578,15 +527,6 @@
}
final Set<ComponentName> add = new HashSet<>(userComponents);
-
- // Remove components from disabled categories so that those services aren't run.
- for (Entry<String, Boolean> e : mCategoryEnabled.entrySet()) {
- if (!e.getValue()) {
- Set<ComponentName> c = queryPackageForServices(null, userIds[i],
- e.getKey());
- add.removeAll(c);
- }
- }
add.removeAll(mSnoozingForCurrentProfiles);
toAdd.put(userIds[i], add);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f20d0a1..2d3ca1f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1496,7 +1496,7 @@
final StatusBarNotification sbn = mNotificationList.get(i).sbn;
if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
&& (sbn.getNotification().flags
- & Notification.FLAG_AUTOGROUP_SUMMARY) != 0) {
+ & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
// We could pass back a cloneLight() but clients might get confused and
// try to send this thing back to notify() again, which would not work
// very well.
@@ -2009,7 +2009,7 @@
@Override
public ComponentName getEffectsSuppressor() {
enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
- return mEffectsSuppressors.get(0);
+ return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
}
@Override
@@ -2459,10 +2459,10 @@
// Fix the notification as best we can.
try {
- Notification.addFieldsFromContext(getContext().createApplicationContext(
- getContext().getPackageManager().getApplicationInfoAsUser(
- pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId),
- Context.CONTEXT_RESTRICTED), notification);
+ final ApplicationInfo ai = getContext().getPackageManager().getApplicationInfoAsUser(
+ pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+ (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
+ Notification.addFieldsFromContext(ai, userId, notification);
} catch (NameNotFoundException e) {
Slog.e(TAG, "Cannot create a context for sending app", e);
return;
@@ -2543,18 +2543,13 @@
// Handle grouped notifications and bail out early if we
// can to avoid extracting signals.
handleGroupedNotificationLocked(r, old, callingUid, callingPid);
- boolean ignoreNotification =
- removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);
- if (DBG) Slog.d(TAG, "ignoreNotification is " + ignoreNotification);
// This conditional is a dirty hack to limit the logging done on
// behalf of the download manager without affecting other apps.
if (!pkg.equals("com.android.providers.downloads")
|| Log.isLoggable("DownloadManager", Log.VERBOSE)) {
int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
- if (ignoreNotification) {
- enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
- } else if (old != null) {
+ if (old != null) {
enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
}
EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
@@ -2562,10 +2557,6 @@
enqueueStatus);
}
- if (ignoreNotification) {
- return;
- }
-
mRankingHelper.extractSignals(r);
final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
@@ -2681,58 +2672,6 @@
}
}
- /**
- * Performs group notification optimizations if SysUI is the only active
- * notification listener and returns whether the given notification should
- * be ignored.
- *
- * <p>Returns true if the given notification is a child of a group with a
- * summary, which means that SysUI will never show it, and hence the new
- * notification can be safely ignored. Also cancels any previous instance
- * of the ignored notification.</p>
- *
- * <p>For summaries, cancels all children of that group, as SysUI will
- * never show them anymore.</p>
- *
- * @return true if the given notification can be ignored as an optimization
- */
- private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r,
- NotificationRecord old, int callingUid, int callingPid) {
- if (!ENABLE_CHILD_NOTIFICATIONS) {
- // No optimizations are possible if listeners want groups.
- if (mListeners.notificationGroupsDesired()) {
- return false;
- }
-
- StatusBarNotification sbn = r.sbn;
- String group = sbn.getGroupKey();
- boolean isSummary = sbn.getNotification().isGroupSummary();
- boolean isChild = !isSummary && sbn.isGroup();
-
- NotificationRecord summary = mSummaryByGroupKey.get(group);
- if (isChild && summary != null) {
- // Child with an active summary -> ignore
- if (DBG) {
- Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary "
- + summary.getKey());
- }
- // Make sure we don't leave an old version of the notification around.
- if (old != null) {
- if (DBG) {
- Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey());
- }
- cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION);
- }
- return true;
- } else if (isSummary) {
- // Summary -> cancel children
- cancelGroupChildrenLocked(r, callingUid, callingPid, null,
- REASON_GROUP_OPTIMIZATION);
- }
- }
- return false;
- }
-
@VisibleForTesting
void buzzBeepBlinkLocked(NotificationRecord record) {
boolean buzz = false;
@@ -3851,7 +3790,7 @@
return;
}
Set<ComponentName> rankerComponents = queryPackageForServices(
- mRankerServicePackageName, UserHandle.USER_SYSTEM, null);
+ mRankerServicePackageName, UserHandle.USER_SYSTEM);
Iterator<ComponentName> iterator = rankerComponents.iterator();
if (iterator.hasNext()) {
ComponentName rankerComponent = iterator.next();
@@ -3869,7 +3808,6 @@
public class NotificationListeners extends ManagedServices {
private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
- private boolean mNotificationGroupsDesired;
public NotificationListeners() {
super(getContext(), mHandler, mNotificationList, mUserProfiles);
@@ -3902,7 +3840,6 @@
final INotificationListener listener = (INotificationListener) info.service;
final NotificationRankingUpdate update;
synchronized (mNotificationList) {
- updateNotificationGroupsDesiredLocked();
update = makeRankingUpdateLocked(info);
}
try {
@@ -3919,7 +3856,6 @@
updateEffectsSuppressorLocked();
}
mLightTrimListeners.remove(removed);
- updateNotificationGroupsDesiredLocked();
}
public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
@@ -4112,31 +4048,6 @@
}
return false;
}
-
- /**
- * Returns whether any of the currently registered listeners wants to receive notification
- * groups.
- *
- * <p>Currently we assume groups are desired by non-SystemUI listeners.</p>
- */
- public boolean notificationGroupsDesired() {
- return mNotificationGroupsDesired;
- }
-
- private void updateNotificationGroupsDesiredLocked() {
- mNotificationGroupsDesired = true;
- // No listeners, no groups.
- if (mServices.isEmpty()) {
- mNotificationGroupsDesired = false;
- return;
- }
- // One listener: Check whether it's SysUI.
- if (mServices.size() == 1 &&
- mServices.get(0).component.getPackageName().equals("com.android.systemui")) {
- mNotificationGroupsDesired = false;
- return;
- }
- }
}
public static final class DumpFilter {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index a89a422..f29970c 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -176,10 +176,7 @@
mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
mCreationTimeMs = previous.mCreationTimeMs;
mVisibleSinceMs = previous.mVisibleSinceMs;
- mUserImportance = previous.mUserImportance;
- mImportance = previous.mImportance;
- mImportanceExplanation = previous.mImportanceExplanation;
- // Don't copy mGlobalSortKey, recompute it.
+ // Don't copy importance information or mGlobalSortKey, recompute them.
}
public Notification getNotification() { return sbn.getNotification(); }
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index eae2eaa..f6255af 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
+
import android.app.AlarmManager;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
@@ -23,6 +25,9 @@
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
import android.os.ServiceManager;
import android.util.ArraySet;
import android.util.Log;
@@ -38,7 +43,9 @@
static final long RETRY_LATENCY = 4 * AlarmManager.INTERVAL_HOUR;
- static final int BACKGROUND_DEXOPT_JOB = 800;
+ static final int JOB_IDLE_OPTIMIZE = 800;
+ static final int JOB_POST_BOOT_UPDATE = 801;
+
private static ComponentName sDexoptServiceName = new ComponentName(
"android",
BackgroundDexOptService.class.getName());
@@ -48,66 +55,193 @@
*/
static final ArraySet<String> sFailedPackageNames = new ArraySet<String>();
- final AtomicBoolean mIdleTime = new AtomicBoolean(false);
+ /**
+ * Atomics set to true if the JobScheduler requests an abort.
+ */
+ final AtomicBoolean mAbortPostBootUpdate = new AtomicBoolean(false);
+ final AtomicBoolean mAbortIdleOptimization = new AtomicBoolean(false);
+
+ /**
+ * Atomic set to true if one job should exit early because another job was started.
+ */
+ final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false);
public static void schedule(Context context) {
JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
- JobInfo job = new JobInfo.Builder(BACKGROUND_DEXOPT_JOB, sDexoptServiceName)
- .setRequiresDeviceIdle(true)
- .setRequiresCharging(true)
- .setPeriodic(TimeUnit.DAYS.toMillis(1))
- .build();
- js.schedule(job);
+
+ // Schedule a one-off job which scans installed packages and updates
+ // out-of-date oat files.
+ js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
+ .setMinimumLatency(TimeUnit.MINUTES.toMillis(1))
+ .setOverrideDeadline(TimeUnit.MINUTES.toMillis(1))
+ .build());
+
+ // Schedule a daily job which scans installed packages and compiles
+ // those with fresh profiling data.
+ js.schedule(new JobInfo.Builder(JOB_IDLE_OPTIMIZE, sDexoptServiceName)
+ .setRequiresDeviceIdle(true)
+ .setRequiresCharging(true)
+ .setPeriodic(TimeUnit.DAYS.toMillis(1))
+ .build());
+
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Jobs scheduled");
+ }
}
- @Override
- public boolean onStartJob(JobParameters params) {
- Log.i(TAG, "onStartJob");
- final PackageManagerService pm =
- (PackageManagerService)ServiceManager.getService("package");
-
- if (pm.isStorageLow()) {
- Log.i(TAG, "Low storage, skipping this run");
- return false;
+ public static void notifyPackageChanged(String packageName) {
+ // The idle maintanance job skips packages which previously failed to
+ // compile. The given package has changed and may successfully compile
+ // now. Remove it from the list of known failing packages.
+ synchronized (sFailedPackageNames) {
+ sFailedPackageNames.remove(packageName);
}
- final ArraySet<String> pkgs = pm.getOptimizablePackages();
- if (pkgs == null || pkgs.isEmpty()) {
- Log.i(TAG, "No packages to optimize");
+ }
+
+ // Returns the current battery level as a 0-100 integer.
+ private int getBatteryLevel() {
+ IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ Intent intent = registerReceiver(null, filter);
+ int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+ int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+
+ if (level < 0 || scale <= 0) {
+ // Battery data unavailable. This should never happen, so assume the worst.
+ return 0;
+ }
+
+ return (100 * level / scale);
+ }
+
+ private boolean runPostBootUpdate(final JobParameters jobParams,
+ final PackageManagerService pm, final ArraySet<String> pkgs) {
+ if (mExitPostBootUpdate.get()) {
+ // This job has already been superseded. Do not start it.
return false;
}
- final JobParameters jobParams = params;
- mIdleTime.set(true);
- new Thread("BackgroundDexOptService_DexOpter") {
+ // Load low battery threshold from the system config. This is a 0-100 integer.
+ final int lowBatteryThreshold = getResources().getInteger(
+ com.android.internal.R.integer.config_lowBatteryWarningLevel);
+
+ mAbortPostBootUpdate.set(false);
+ new Thread("BackgroundDexOptService_PostBootUpdate") {
@Override
public void run() {
for (String pkg : pkgs) {
- if (!mIdleTime.get()) {
- // Out of the idle state. Stop the compilation.
+ if (mAbortPostBootUpdate.get()) {
+ // JobScheduler requested an early abort.
+ return;
+ }
+ if (mExitPostBootUpdate.get()) {
+ // Different job, which supersedes this one, is running.
+ break;
+ }
+ if (getBatteryLevel() < lowBatteryThreshold) {
+ // Rather bail than completely drain the battery.
+ break;
+ }
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Updating package " + pkg);
+ }
+ // Update package if needed. Note that there can be no race between concurrent
+ // jobs because PackageDexOptimizer.performDexOpt is synchronized.
+ pm.performDexOpt(pkg,
+ /* instruction set */ null,
+ /* checkProfiles */ false,
+ PackageManagerService.REASON_BOOT,
+ /* force */ false);
+ }
+ // Ran to completion, so we abandon our timeslice and do not reschedule.
+ jobFinished(jobParams, /* reschedule */ false);
+ }
+ }.start();
+ return true;
+ }
+
+ private boolean runIdleOptimization(final JobParameters jobParams,
+ final PackageManagerService pm, final ArraySet<String> pkgs) {
+ // If post-boot update is still running, request that it exits early.
+ mExitPostBootUpdate.set(true);
+
+ mAbortIdleOptimization.set(false);
+ new Thread("BackgroundDexOptService_IdleOptimization") {
+ @Override
+ public void run() {
+ for (String pkg : pkgs) {
+ if (mAbortIdleOptimization.get()) {
+ // JobScheduler requested an early abort.
return;
}
if (sFailedPackageNames.contains(pkg)) {
- // skip previously failing package
+ // Skip previously failing package
continue;
}
- if (!pm.performDexOpt(pkg, /* instruction set */ null, /* checkProfiles */ true,
- PackageManagerService.REASON_BACKGROUND_DEXOPT, /* force */ false)) {
- // there was a problem running dexopt,
- // remember this so we do not keep retrying.
+ // Conservatively add package to the list of failing ones in case performDexOpt
+ // never returns.
+ synchronized (sFailedPackageNames) {
sFailedPackageNames.add(pkg);
}
+ // Optimize package if needed. Note that there can be no race between
+ // concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
+ if (pm.performDexOpt(pkg,
+ /* instruction set */ null,
+ /* checkProfiles */ true,
+ PackageManagerService.REASON_BACKGROUND_DEXOPT,
+ /* force */ false)) {
+ // Dexopt succeeded, remove package from the list of failing ones.
+ synchronized (sFailedPackageNames) {
+ sFailedPackageNames.remove(pkg);
+ }
+ }
}
- // ran to completion, so we abandon our timeslice and do not reschedule
- jobFinished(jobParams, false);
+ // Ran to completion, so we abandon our timeslice and do not reschedule.
+ jobFinished(jobParams, /* reschedule */ false);
}
}.start();
return true;
}
@Override
+ public boolean onStartJob(JobParameters params) {
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "onStartJob");
+ }
+
+ PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
+ if (pm.isStorageLow()) {
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Low storage, skipping this run");
+ }
+ return false;
+ }
+
+ final ArraySet<String> pkgs = pm.getOptimizablePackages();
+ if (pkgs == null || pkgs.isEmpty()) {
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "No packages to optimize");
+ }
+ return false;
+ }
+
+ if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
+ return runPostBootUpdate(params, pm, pkgs);
+ } else {
+ return runIdleOptimization(params, pm, pkgs);
+ }
+ }
+
+ @Override
public boolean onStopJob(JobParameters params) {
- Log.i(TAG, "onIdleStop");
- mIdleTime.set(false);
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "onStopJob");
+ }
+
+ if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
+ mAbortPostBootUpdate.set(true);
+ } else {
+ mAbortIdleOptimization.set(true);
+ }
return false;
}
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 4c18e15..43a0b91 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -147,6 +147,7 @@
@Override
public void addOnAppsChangedListener(String callingPackage, IOnAppsChangedListener listener)
throws RemoteException {
+ verifyCallingPackage(callingPackage);
synchronized (mListeners) {
if (DEBUG) {
Log.d(TAG, "Adding listener from " + Binder.getCallingUserHandle());
@@ -213,8 +214,11 @@
* Checks if the caller is in the same group as the userToCheck.
*/
private void ensureInUserProfiles(UserHandle userToCheck, String message) {
+ ensureInUserProfiles(userToCheck.getIdentifier(), message);
+ }
+
+ private void ensureInUserProfiles(int targetUserId, String message) {
final int callingUserId = injectCallingUserId();
- final int targetUserId = userToCheck.getIdentifier();
if (targetUserId == callingUserId) return;
@@ -253,9 +257,13 @@
* Checks if the user is enabled.
*/
private boolean isUserEnabled(UserHandle user) {
+ return isUserEnabled(user.getIdentifier());
+ }
+
+ private boolean isUserEnabled(int userId) {
long ident = injectClearCallingIdentity();
try {
- UserInfo targetUserInfo = mUm.getUserInfo(user.getIdentifier());
+ UserInfo targetUserInfo = mUm.getUserInfo(userId);
return targetUserInfo != null && targetUserInfo.isEnabled();
} finally {
injectRestoreCallingIdentity(ident);
@@ -286,7 +294,7 @@
}
@Override
- public ResolveInfo resolveActivity(Intent intent, UserHandle user)
+ public ActivityInfo resolveActivity(ComponentName component, UserHandle user)
throws RemoteException {
ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user);
if (!isUserEnabled(user)) {
@@ -295,11 +303,11 @@
long ident = Binder.clearCallingIdentity();
try {
- ResolveInfo app = mPm.resolveActivityAsUser(intent,
+ IPackageManager pm = AppGlobals.getPackageManager();
+ return pm.getActivityInfo(component,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
user.getIdentifier());
- return app;
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -346,8 +354,12 @@
}
private void ensureShortcutPermission(@NonNull String callingPackage, UserHandle user) {
+ ensureShortcutPermission(callingPackage, user.getIdentifier());
+ }
+
+ private void ensureShortcutPermission(@NonNull String callingPackage, int userId) {
verifyCallingPackage(callingPackage);
- ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
+ ensureInUserProfiles(userId, "Cannot start activity for unrelated profile " + userId);
if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
callingPackage)) {
@@ -357,32 +369,24 @@
@Override
public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
- String packageName, ComponentName componentName, int flags, UserHandle user) {
+ String packageName, List shortcutIds, ComponentName componentName, int flags,
+ UserHandle user) {
ensureShortcutPermission(callingPackage, user);
if (!isUserEnabled(user)) {
return new ParceledListSlice<>(new ArrayList(0));
}
+ if (shortcutIds != null && packageName == null) {
+ throw new IllegalArgumentException(
+ "To query by shortcut ID, package name must also be set");
+ }
return new ParceledListSlice<>(
mShortcutServiceInternal.getShortcuts(getCallingUserId(),
- callingPackage, changedSince, packageName,
+ callingPackage, changedSince, packageName, shortcutIds,
componentName, flags, user.getIdentifier()));
}
@Override
- public ParceledListSlice getShortcutInfo(String callingPackage, String packageName,
- List<String> ids, UserHandle user) {
- ensureShortcutPermission(callingPackage, user);
- if (!isUserEnabled(user)) {
- return new ParceledListSlice<>(new ArrayList(0));
- }
-
- return new ParceledListSlice<>(
- mShortcutServiceInternal.getShortcutInfo(getCallingUserId(),
- callingPackage, packageName, ids, user.getIdentifier()));
- }
-
- @Override
public void pinShortcuts(String callingPackage, String packageName, List<String> ids,
UserHandle user) {
ensureShortcutPermission(callingPackage, user);
@@ -396,27 +400,27 @@
}
@Override
- public int getShortcutIconResId(String callingPackage, ShortcutInfo shortcut,
- UserHandle user) {
- ensureShortcutPermission(callingPackage, user);
- if (!isUserEnabled(user)) {
+ public int getShortcutIconResId(String callingPackage, String packageName, String id,
+ int userId) {
+ ensureShortcutPermission(callingPackage, userId);
+ if (!isUserEnabled(userId)) {
return 0;
}
return mShortcutServiceInternal.getShortcutIconResId(getCallingUserId(),
- callingPackage, shortcut, user.getIdentifier());
+ callingPackage, packageName, id, userId);
}
@Override
- public ParcelFileDescriptor getShortcutIconFd(String callingPackage, ShortcutInfo shortcut,
- UserHandle user) {
- ensureShortcutPermission(callingPackage, user);
- if (!isUserEnabled(user)) {
+ public ParcelFileDescriptor getShortcutIconFd(String callingPackage,
+ String packageName, String id, int userId) {
+ ensureShortcutPermission(callingPackage, userId);
+ if (!isUserEnabled(userId)) {
return null;
}
return mShortcutServiceInternal.getShortcutIconFd(getCallingUserId(),
- callingPackage, shortcut, user.getIdentifier());
+ callingPackage, packageName, id, userId);
}
@Override
@@ -428,23 +432,23 @@
@Override
public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
- Rect sourceBounds, Bundle startActivityOptions, UserHandle user) {
+ Rect sourceBounds, Bundle startActivityOptions, int userId) {
verifyCallingPackage(callingPackage);
- ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
+ ensureInUserProfiles(userId, "Cannot start activity for unrelated profile " + userId);
- if (!isUserEnabled(user)) {
+ if (!isUserEnabled(userId)) {
throw new IllegalStateException("Cannot start a shortcut for disabled profile "
- + user);
+ + userId);
}
// Even without the permission, pinned shortcuts are always launchable.
if (!mShortcutServiceInternal.isPinnedByCaller(getCallingUserId(),
- callingPackage, packageName, shortcutId, user.getIdentifier())) {
- ensureShortcutPermission(callingPackage, user);
+ callingPackage, packageName, shortcutId, userId)) {
+ ensureShortcutPermission(callingPackage, userId);
}
final Intent intent = mShortcutServiceInternal.createShortcutIntent(getCallingUserId(),
- callingPackage, packageName, shortcutId, user.getIdentifier());
+ callingPackage, packageName, shortcutId, userId);
if (intent == null) {
return false;
}
@@ -455,7 +459,7 @@
final long ident = Binder.clearCallingIdentity();
try {
- mContext.startActivityAsUser(intent, startActivityOptions, user);
+ mContext.startActivityAsUser(intent, startActivityOptions, UserHandle.of(userId));
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -768,7 +772,8 @@
final List<ShortcutInfo> list =
mShortcutServiceInternal.getShortcuts(launcherUserId,
cookie.packageName,
- /* changedSince= */ 0, packageName, /* component= */ null,
+ /* changedSince= */ 0, packageName, /* shortcutIds=*/ null,
+ /* component= */ null,
ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
| ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index d13f472..b3ac05c 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -53,7 +53,6 @@
// TODO b/19550105 Remove error codes and use exceptions
static final int DEX_OPT_SKIPPED = 0;
static final int DEX_OPT_PERFORMED = 1;
- static final int DEX_OPT_DEFERRED = 2;
static final int DEX_OPT_FAILED = -1;
private final Installer mInstaller;
@@ -170,6 +169,8 @@
final boolean debuggable = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
boolean performedDexOpt = false;
+ boolean successfulDexOpt = true;
+
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
for (String path : paths) {
@@ -226,15 +227,20 @@
performedDexOpt = true;
} catch (InstallerException e) {
Slog.w(TAG, "Failed to dexopt", e);
+ successfulDexOpt = false;
}
}
}
- // If we've gotten here, we're sure that no error occurred and that we haven't
- // deferred dex-opt. We've either dex-opted one more paths or instruction sets or
- // we've skipped all of them because they are up to date. In both cases this
- // package doesn't need dexopt any longer.
- return performedDexOpt ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
+ if (successfulDexOpt) {
+ // If we've gotten here, we're sure that no error occurred. We've either
+ // dex-opted one or more paths or instruction sets or we've skipped
+ // all of them because they are up to date. In both cases this package
+ // doesn't need dexopt any longer.
+ return performedDexOpt ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
+ } else {
+ return DEX_OPT_FAILED;
+ }
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ddccbb3..5aa30ca 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -229,6 +229,7 @@
import com.android.internal.os.InstallerConnection.InstallerException;
import com.android.internal.os.SomeArgs;
import com.android.internal.os.Zygote;
+import com.android.internal.telephony.CarrierAppUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
@@ -2051,6 +2052,10 @@
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
+ // Disable any carrier apps. We do this very early in boot to prevent the apps from being
+ // disabled after already being started.
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(context.getOpPackageName(), m,
+ UserHandle.USER_SYSTEM);
ServiceManager.addService("package", m);
return m;
}
@@ -4716,27 +4721,39 @@
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
- if (!sUserManager.exists(userId)) return null;
- flags = updateFlagsForResolve(flags, userId, intent);
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
- false /* requireFullPermission */, false /* checkShell */, "resolve intent");
- final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
- userId);
- final ResolveInfo bestChoice =
- chooseBestActivity(intent, resolvedType, flags, query, userId);
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
- if (isEphemeralAllowed(intent, query, userId)) {
- final EphemeralResolveInfo ai =
- getEphemeralResolveInfo(intent, resolvedType, userId);
- if (ai != null) {
- if (DEBUG_EPHEMERAL) {
- Slog.v(TAG, "Returning an EphemeralResolveInfo");
+ if (!sUserManager.exists(userId)) return null;
+ flags = updateFlagsForResolve(flags, userId, intent);
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
+
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
+ final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType,
+ flags, userId);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+ final ResolveInfo bestChoice =
+ chooseBestActivity(intent, resolvedType, flags, query, userId);
+
+ if (isEphemeralAllowed(intent, query, userId)) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
+ final EphemeralResolveInfo ai =
+ getEphemeralResolveInfo(intent, resolvedType, userId);
+ if (ai != null) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.v(TAG, "Returning an EphemeralResolveInfo");
+ }
+ bestChoice.ephemeralInstaller = mEphemeralInstallerInfo;
+ bestChoice.ephemeralResolveInfo = ai;
}
- bestChoice.ephemeralInstaller = mEphemeralInstallerInfo;
- bestChoice.ephemeralResolveInfo = ai;
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
+ return bestChoice;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- return bestChoice;
}
@Override
@@ -4905,6 +4922,7 @@
}
ri = new ResolveInfo(mResolveInfo);
ri.activityInfo = new ActivityInfo(ri.activityInfo);
+ ri.activityInfo.labelRes = ResolverActivity.getLabelRes(intent.getAction());
ri.activityInfo.applicationInfo = new ApplicationInfo(
ri.activityInfo.applicationInfo);
if (userId != 0) {
@@ -5183,8 +5201,14 @@
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) {
- return new ParceledListSlice<>(
- queryIntentActivitiesInternal(intent, resolvedType, flags, userId));
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
+
+ return new ParceledListSlice<>(
+ queryIntentActivitiesInternal(intent, resolvedType, flags, userId));
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
@@ -7147,6 +7171,8 @@
}
}
+ // Run dexopt on a given package. Returns true if dexopt did not fail, i.e.
+ // if the package can now be considered up to date for the given filter.
private boolean performDexOptInternal(String packageName, String instructionSet,
boolean checkProfiles, String targetCompilerFilter, boolean force) {
PackageParser.Package p;
@@ -7167,7 +7193,7 @@
final String[] instructionSets = new String[] { targetInstructionSet };
int result = performDexOptInternalWithDependenciesLI(p, instructionSets,
checkProfiles, targetCompilerFilter, force);
- return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
+ return result != PackageDexOptimizer.DEX_OPT_FAILED;
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -13646,6 +13672,16 @@
}
}
+ // Check for shared user id changes
+ String invalidPackageName =
+ getParentOrChildPackageChangedSharedUser(oldPackage, pkg);
+ if (invalidPackageName != null) {
+ res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
+ "Package " + invalidPackageName + " tried to change user "
+ + oldPackage.mSharedUserId);
+ return;
+ }
+
// In case of rollback, remember per-user/profile install state
allUsers = sUserManager.getUserIds();
}
@@ -13895,15 +13931,6 @@
setInstallAndUpdateTime(newPackage, deletedPkgSetting.firstInstallTime,
System.currentTimeMillis());
- // Check for shared user id changes
- String invalidPackageName = getParentOrChildPackageChangedSharedUser(
- deletedPackage, newPackage);
- if (invalidPackageName != null) {
- res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
- "Forbidding shared user change from " + deletedPkgSetting.sharedUser
- + " to " + invalidPackageName);
- }
-
// Update the package dynamic state if succeeded
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
// Now that the install succeeded make sure we remove data
@@ -14514,7 +14541,6 @@
return;
}
-
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
// Do not run PackageDexOptimizer through the local performDexOpt
// method because `pkg` is not in `mPackages` yet.
@@ -14522,10 +14548,15 @@
false /* checkProfiles */, getCompilerFilterForReason(REASON_INSTALL));
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
- String msg = "Extracking package failed for " + pkgName;
+ String msg = "Extracting package failed for " + pkgName;
res.setError(INSTALL_FAILED_DEXOPT, msg);
return;
}
+
+ // Notify BackgroundDexOptService that the package has been changed.
+ // If this is an update of a package which used to fail to compile,
+ // BDOS will remove it from its blacklist.
+ BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
}
if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
@@ -16002,16 +16033,25 @@
@Override
public void deleteApplicationCacheFiles(final String packageName,
final IPackageDataObserver observer) {
+ final int userId = UserHandle.getCallingUserId();
+ deleteApplicationCacheFilesAsUser(packageName, userId, observer);
+ }
+
+ @Override
+ public void deleteApplicationCacheFilesAsUser(final String packageName, final int userId,
+ final IPackageDataObserver observer) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_CACHE_FILES, null);
- // Queue up an async operation since the package deletion may take a little while.
- final int userId = UserHandle.getCallingUserId();
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ /* requireFullPermission= */ true, /* checkShell= */ false,
+ "delete application cache files");
final PackageParser.Package pkg;
synchronized (mPackages) {
pkg = mPackages.get(packageName);
}
+ // Queue up an async operation since the package deletion may take a little while.
mHandler.post(new Runnable() {
public void run() {
try (PackageFreezer freezer = freezePackage(packageName,
@@ -16068,11 +16108,17 @@
mInstaller.getAppSize(ps.volumeUuid, packageName, userId,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE,
ps.getCeDataInode(userId), ps.codePathString, stats);
- return true;
} catch (InstallerException e) {
Slog.w(TAG, String.valueOf(e));
return false;
}
+
+ // For now, ignore code size of packages on system partition
+ if (isSystemApp(ps) && !isUpdatedSystemApp(ps)) {
+ stats.codeSize = 0;
+ }
+
+ return true;
}
private int getUidTargetSdkVersionLockedLPr(int uid) {
@@ -17032,8 +17078,13 @@
}
PackageSetting pkgSetting;
final int uid = Binder.getCallingUid();
- final int permission = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
+ final int permission;
+ if (uid == Process.SYSTEM_UID) {
+ permission = PackageManager.PERMISSION_GRANTED;
+ } else {
+ permission = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
+ }
enforceCrossUserPermission(uid, userId,
false /* requireFullPermission */, true /* checkShell */, "set enabled");
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 847f993..7debf9b 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2686,7 +2686,7 @@
void writePermissionLPr(XmlSerializer serializer, BasePermission bp)
throws XmlPullParserException, java.io.IOException {
- if (bp.type != BasePermission.TYPE_BUILTIN && bp.sourcePackage != null) {
+ if (bp.sourcePackage != null) {
serializer.startTag(null, TAG_ITEM);
serializer.attribute(null, ATTR_NAME, bp.name);
serializer.attribute(null, "package", bp.sourcePackage);
@@ -3348,8 +3348,12 @@
final String ptype = parser.getAttributeValue(null, "type");
if (name != null && sourcePackage != null) {
final boolean dynamic = "dynamic".equals(ptype);
- final BasePermission bp = new BasePermission(name.intern(), sourcePackage,
- dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL);
+ BasePermission bp = out.get(name);
+ // If the permission is builtin, do not clobber it.
+ if (bp == null || bp.type != BasePermission.TYPE_BUILTIN) {
+ bp = new BasePermission(name.intern(), sourcePackage,
+ dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL);
+ }
bp.protectionLevel = readInt(parser, null, "protection",
PermissionInfo.PROTECTION_NORMAL);
bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 1076a7a..b9b65eb 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
@@ -27,6 +28,7 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -36,7 +38,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collection;
+import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
@@ -50,6 +52,7 @@
private static final String TAG_INTENT_EXTRAS = "intent-extras";
private static final String TAG_EXTRAS = "extras";
private static final String TAG_SHORTCUT = "shortcut";
+ private static final String TAG_CATEGORIES = "categories";
private static final String ATTR_NAME = "name";
private static final String ATTR_DYNAMIC_COUNT = "dynamic-count";
@@ -66,6 +69,11 @@
private static final String ATTR_ICON_RES = "icon-res";
private static final String ATTR_BITMAP_PATH = "bitmap-path";
+ private static final String NAME_CATEGORIES = "categories";
+
+ private static final String TAG_STRING_ARRAY_XMLUTILS = "string-array";
+ private static final String ATTR_NAME_XMLUTILS = "name";
+
/**
* All the shortcuts from the package, keyed on IDs.
*/
@@ -304,7 +312,7 @@
* and return true. Otherwise just return false.
*/
public boolean tryApiCall(@NonNull ShortcutService s) {
- if (getApiCallCount(s) >= s.mMaxDailyUpdates) {
+ if (getApiCallCount(s) >= s.mMaxUpdatesPerInterval) {
return false;
}
mApiCallCount++;
@@ -484,6 +492,16 @@
ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath());
}
+ {
+ final List<String> cat = si.getCategories();
+ if (cat != null && cat.size() > 0) {
+ out.startTag(null, TAG_CATEGORIES);
+ XmlUtils.writeStringArrayXml(cat.toArray(new String[cat.size()]),
+ NAME_CATEGORIES, out);
+ out.endTag(null, TAG_CATEGORIES);
+ }
+ }
+
ShortcutService.writeTagExtra(out, TAG_INTENT_EXTRAS,
si.getIntentPersistableExtras());
ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
@@ -522,7 +540,7 @@
ret.getPackageInfo().loadFromXml(parser, fromBackup);
continue;
case TAG_SHORTCUT:
- final ShortcutInfo si = parseShortcut(parser, packageName);
+ final ShortcutInfo si = parseShortcut(parser, packageName, ownerUserId);
// Don't use addShortcut(), we don't need to save the icon.
ret.mShortcuts.put(si.getId(), si);
@@ -534,8 +552,8 @@
return ret;
}
- private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName)
- throws IOException, XmlPullParserException {
+ private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName,
+ @UserIdInt int userId) throws IOException, XmlPullParserException {
String id;
ComponentName activityComponent;
// Icon icon;
@@ -549,6 +567,7 @@
int flags;
int iconRes;
String bitmapPath;
+ String[] categories = null;
id = ShortcutService.parseStringAttribute(parser, ATTR_ID);
activityComponent = ShortcutService.parseComponentNameAttribute(parser,
@@ -582,11 +601,21 @@
case TAG_EXTRAS:
extras = PersistableBundle.restoreFromXml(parser);
continue;
+ case TAG_CATEGORIES:
+ // This just contains string-array.
+ continue;
+ case TAG_STRING_ARRAY_XMLUTILS:
+ if (NAME_CATEGORIES.equals(ShortcutService.parseStringAttribute(parser,
+ ATTR_NAME_XMLUTILS))) {
+ categories = XmlUtils.readThisStringArrayXml(parser, TAG_STRING_ARRAY_XMLUTILS, null);
+ }
+ continue;
}
throw ShortcutService.throwForInvalidTag(depth, tag);
}
return new ShortcutInfo(
- id, packageName, activityComponent, /* icon =*/ null, title, text, intent,
+ userId, id, packageName, activityComponent, /* icon =*/ null, title, text,
+ (categories == null ? null : Arrays.asList(categories)), intent,
intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
iconRes, bitmapPath);
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 5c1e7a8..ac6510a 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -126,10 +126,10 @@
static final boolean DEBUG_LOAD = false; // STOPSHIP if true
@VisibleForTesting
- static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
+ static final long DEFAULT_RESET_INTERVAL_SEC = 60 * 60; // 1 hour
@VisibleForTesting
- static final int DEFAULT_MAX_DAILY_UPDATES = 10;
+ static final int DEFAULT_MAX_UPDATES_PER_INTERVAL = 2;
@VisibleForTesting
static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
@@ -180,7 +180,7 @@
/**
* Key name for the max number of modifying API calls per app for every interval. (int)
*/
- String KEY_MAX_DAILY_UPDATES = "max_daily_updates";
+ String KEY_MAX_UPDATES_PER_INTERVAL = "max_updates_per_interval";
/**
* Key name for the max icon dimensions in DP, for non-low-memory devices.
@@ -232,9 +232,9 @@
private int mMaxDynamicShortcuts;
/**
- * Max number of updating API calls that each application can make a day.
+ * Max number of updating API calls that each application can make during the interval.
*/
- int mMaxDailyUpdates;
+ int mMaxUpdatesPerInterval;
/**
* Actual throttling-reset interval. By default it's a day.
@@ -419,26 +419,26 @@
result = false;
}
- mSaveDelayMillis = (int) parser.getLong(ConfigConstants.KEY_SAVE_DELAY_MILLIS,
- DEFAULT_SAVE_DELAY_MS);
+ mSaveDelayMillis = Math.max(0, (int) parser.getLong(ConfigConstants.KEY_SAVE_DELAY_MILLIS,
+ DEFAULT_SAVE_DELAY_MS));
- mResetInterval = parser.getLong(
+ mResetInterval = Math.max(1, parser.getLong(
ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC)
- * 1000L;
+ * 1000L);
- mMaxDailyUpdates = (int) parser.getLong(
- ConfigConstants.KEY_MAX_DAILY_UPDATES, DEFAULT_MAX_DAILY_UPDATES);
+ mMaxUpdatesPerInterval = Math.max(0, (int) parser.getLong(
+ ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL, DEFAULT_MAX_UPDATES_PER_INTERVAL));
- mMaxDynamicShortcuts = (int) parser.getLong(
- ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP);
+ mMaxDynamicShortcuts = Math.max(0, (int) parser.getLong(
+ ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP));
- final int iconDimensionDp = injectIsLowRamDevice()
+ final int iconDimensionDp = Math.max(1, injectIsLowRamDevice()
? (int) parser.getLong(
ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM,
DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP)
: (int) parser.getLong(
ConfigConstants.KEY_MAX_ICON_DIMENSION_DP,
- DEFAULT_MAX_ICON_DIMENSION_DP);
+ DEFAULT_MAX_ICON_DIMENSION_DP));
mMaxIconDimension = injectDipToPixel(iconDimensionDp);
@@ -968,7 +968,8 @@
return; // has no icon
}
- Bitmap bitmap = null;
+ Bitmap bitmap;
+ Bitmap bitmapToRecycle = null;
try {
switch (icon.getType()) {
case Icon.TYPE_RESOURCE: {
@@ -979,7 +980,7 @@
return;
}
case Icon.TYPE_BITMAP: {
- bitmap = icon.getBitmap();
+ bitmap = icon.getBitmap(); // Don't recycle in this case.
break;
}
case Icon.TYPE_URI: {
@@ -987,7 +988,8 @@
try (InputStream is = mContext.getContentResolver().openInputStream(uri)) {
- bitmap = BitmapFactory.decodeStream(is);
+ bitmapToRecycle = BitmapFactory.decodeStream(is);
+ bitmap = bitmapToRecycle;
} catch (IOException e) {
Slog.e(TAG, "Unable to load icon from " + uri);
@@ -1011,8 +1013,14 @@
try {
path = out.getFile();
- shrinkBitmap(bitmap, mMaxIconDimension)
- .compress(mIconPersistFormat, mIconPersistQuality, out);
+ Bitmap shrunk = shrinkBitmap(bitmap, mMaxIconDimension);
+ try {
+ shrunk.compress(mIconPersistFormat, mIconPersistQuality, out);
+ } finally {
+ if (bitmap != shrunk) {
+ shrunk.recycle();
+ }
+ }
shortcut.setBitmapPath(out.getFile().getAbsolutePath());
shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_FILE);
@@ -1027,8 +1035,8 @@
}
}
} finally {
- if (bitmap != null) {
- bitmap.recycle();
+ if (bitmapToRecycle != null) {
+ bitmapToRecycle.recycle();
}
// Once saved, we won't use the original icon information, so null it out.
shortcut.clearIcon();
@@ -1076,8 +1084,6 @@
c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null);
- in.recycle();
-
return scaledBitmap;
}
@@ -1122,7 +1128,7 @@
if (injectGetPackageUid(packageName, userId) == injectBinderCallingUid()) {
return; // Caller is valid.
}
- throw new SecurityException("Caller UID= doesn't own " + packageName);
+ throw new SecurityException("Calling package name mismatch");
}
void postToHandler(Runnable r) {
@@ -1313,10 +1319,13 @@
}
@Override
- public boolean addDynamicShortcut(String packageName, ShortcutInfo newShortcut,
+ public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
+ final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
+ final int size = newShortcuts.size();
+
synchronized (mLock) {
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
@@ -1324,12 +1333,15 @@
if (!ps.tryApiCall(this)) {
return false;
}
+ for (int i = 0; i < size; i++) {
+ final ShortcutInfo newShortcut = newShortcuts.get(i);
- // Validate the shortcut.
- fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
+ // Validate the shortcut.
+ fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
- // Add it.
- ps.addDynamicShortcut(this, newShortcut);
+ // Add it.
+ ps.addDynamicShortcut(this, newShortcut);
+ }
}
userPackageChanged(packageName, userId);
@@ -1337,19 +1349,22 @@
}
@Override
- public void deleteDynamicShortcut(String packageName, String shortcutId,
+ public void removeDynamicShortcuts(String packageName, List shortcutIds,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
- Preconditions.checkStringNotEmpty(shortcutId, "shortcutId must be provided");
+ Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
synchronized (mLock) {
- getPackageShortcutsLocked(packageName, userId).deleteDynamicWithId(this, shortcutId);
+ for (int i = shortcutIds.size() - 1; i >= 0; i--) {
+ getPackageShortcutsLocked(packageName, userId).deleteDynamicWithId(this,
+ Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)));
+ }
}
userPackageChanged(packageName, userId);
}
@Override
- public void deleteAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
+ public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
verifyCaller(packageName, userId);
synchronized (mLock) {
@@ -1403,7 +1418,7 @@
verifyCaller(packageName, userId);
synchronized (mLock) {
- return mMaxDailyUpdates
+ return mMaxUpdatesPerInterval
- getPackageShortcutsLocked(packageName, userId).getApiCallCount(this);
}
}
@@ -1419,6 +1434,8 @@
@Override
public int getIconMaxDimensions(String packageName, int userId) throws RemoteException {
+ verifyCaller(packageName, userId);
+
synchronized (mLock) {
return mMaxIconDimension;
}
@@ -1439,7 +1456,15 @@
getUserShortcutsLocked(userId).resetThrottling();
}
scheduleSaveUser(userId);
- Slog.i(TAG, "ShortcutManager: throttling counter reset");
+ Slog.i(TAG, "ShortcutManager: throttling counter reset for user " + userId);
+ }
+
+ void resetAllThrottlingInner() {
+ synchronized (mLock) {
+ mRawLastResetTime = injectCurrentTimeMillis();
+ }
+ scheduleSaveBaseState();
+ Slog.i(TAG, "ShortcutManager: throttling counter reset for all users");
}
// We override this method in unit tests to do a simpler check.
@@ -1522,14 +1547,20 @@
// === House keeping ===
+ @VisibleForTesting
+ void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) {
+ cleanUpPackageLocked(packageName, owningUserId, packageUserId,
+ /* forceForCommandLine= */ false);
+ }
+
/**
* Remove all the information associated with a package. This will really remove all the
* information, including the restore information (i.e. it'll remove packages even if they're
* shadow).
*/
- @VisibleForTesting
- void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) {
- if (isPackageInstalled(packageName, packageUserId)) {
+ private void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId,
+ boolean forceForCommandLine) {
+ if (!forceForCommandLine && isPackageInstalled(packageName, packageUserId)) {
wtf("Package " + packageName + " is still installed for user " + packageUserId);
return;
}
@@ -1580,13 +1611,17 @@
@Override
public List<ShortcutInfo> getShortcuts(int launcherUserId,
@NonNull String callingPackage, long changedSince,
- @Nullable String packageName, @Nullable ComponentName componentName,
+ @Nullable String packageName, @Nullable List<String> shortcutIds,
+ @Nullable ComponentName componentName,
int queryFlags, int userId) {
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
final int cloneFlag =
((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) == 0)
? ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER
: ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
+ if (packageName == null) {
+ shortcutIds = null; // LauncherAppsService already threw for it though.
+ }
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
@@ -1594,14 +1629,14 @@
if (packageName != null) {
getShortcutsInnerLocked(launcherUserId,
- callingPackage, packageName, changedSince,
+ callingPackage, packageName, shortcutIds, changedSince,
componentName, queryFlags, userId, ret, cloneFlag);
} else {
final ArrayMap<String, ShortcutPackage> packages =
getUserShortcutsLocked(userId).getAllPackages();
for (int i = packages.size() - 1; i >= 0; i--) {
getShortcutsInnerLocked(launcherUserId,
- callingPackage, packages.keyAt(i), changedSince,
+ callingPackage, packages.keyAt(i), shortcutIds, changedSince,
componentName, queryFlags, userId, ret, cloneFlag);
}
}
@@ -1610,14 +1645,20 @@
}
private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
- @Nullable String packageName,long changedSince,
+ @Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince,
@Nullable ComponentName componentName, int queryFlags,
int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
+ final ArraySet<String> ids = shortcutIds == null ? null
+ : new ArraySet<>(shortcutIds);
+
getPackageShortcutsLocked(packageName, userId).findAll(ShortcutService.this, ret,
(ShortcutInfo si) -> {
if (si.getLastChangedTimestamp() < changedSince) {
return false;
}
+ if (ids != null && !ids.contains(si.getId())) {
+ return false;
+ }
if (componentName != null
&& !componentName.equals(si.getActivityComponent())) {
return false;
@@ -1633,27 +1674,6 @@
}
@Override
- public List<ShortcutInfo> getShortcutInfo(int launcherUserId,
- @NonNull String callingPackage,
- @NonNull String packageName, @Nullable List<String> ids, int userId) {
- // Calling permission must be checked by LauncherAppsImpl.
- Preconditions.checkStringNotEmpty(packageName, "packageName");
-
- final ArrayList<ShortcutInfo> ret = new ArrayList<>(ids.size());
- final ArraySet<String> idSet = new ArraySet<>(ids);
- synchronized (mLock) {
- getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
- .attemptToRestoreIfNeededAndSave(ShortcutService.this);
-
- getPackageShortcutsLocked(packageName, userId).findAll(
- ShortcutService.this, ret,
- (ShortcutInfo si) -> idSet.contains(si.getId()),
- ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER, callingPackage, launcherUserId);
- }
- return ret;
- }
-
- @Override
public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId) {
Preconditions.checkStringNotEmpty(packageName, "packageName");
@@ -1733,17 +1753,18 @@
}
@Override
- public int getShortcutIconResId(int launcherUserId,
- @NonNull String callingPackage,
- @NonNull ShortcutInfo shortcut, int userId) {
- Preconditions.checkNotNull(shortcut, "shortcut");
+ public int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ Preconditions.checkNotNull(callingPackage, "callingPackage");
+ Preconditions.checkNotNull(packageName, "packageName");
+ Preconditions.checkNotNull(shortcutId, "shortcutId");
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave(ShortcutService.this);
final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
- shortcut.getPackageName(), userId).findShortcutById(shortcut.getId());
+ packageName, userId).findShortcutById(shortcutId);
return (shortcutInfo != null && shortcutInfo.hasIconResource())
? shortcutInfo.getIconResourceId() : 0;
}
@@ -1751,16 +1772,18 @@
@Override
public ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
- @NonNull String callingPackage,
- @NonNull ShortcutInfo shortcutIn, int userId) {
- Preconditions.checkNotNull(shortcutIn, "shortcut");
+ @NonNull String callingPackage, @NonNull String packageName,
+ @NonNull String shortcutId, int userId) {
+ Preconditions.checkNotNull(callingPackage, "callingPackage");
+ Preconditions.checkNotNull(packageName, "packageName");
+ Preconditions.checkNotNull(shortcutId, "shortcutId");
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave(ShortcutService.this);
final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
- shortcutIn.getPackageName(), userId).findShortcutById(shortcutIn.getId());
+ packageName, userId).findShortcutById(shortcutId);
if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
return null;
}
@@ -1865,9 +1888,15 @@
Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName,
packageUserId));
}
+ handlePackageRemovedInner(packageName, packageUserId, /* forceForCommandLine =*/ false);
+ }
+
+ private void handlePackageRemovedInner(String packageName, @UserIdInt int packageUserId,
+ boolean forceForCommandLine) {
synchronized (mLock) {
forEachLoadedUserLocked(user ->
- cleanUpPackageLocked(packageName, user.getUserId(), packageUserId));
+ cleanUpPackageLocked(packageName, user.getUserId(), packageUserId,
+ forceForCommandLine));
}
}
@@ -2048,17 +2077,26 @@
pw.print(formatTime(next));
pw.println();
- pw.print(" Max icon dim: ");
- pw.print(mMaxIconDimension);
- pw.print(" Icon format: ");
- pw.print(mIconPersistFormat);
- pw.print(" Icon quality: ");
+ pw.print(" Config:");
+ pw.print(" Max icon dim: ");
+ pw.println(mMaxIconDimension);
+ pw.print(" Icon format: ");
+ pw.println(mIconPersistFormat);
+ pw.print(" Icon quality: ");
pw.println(mIconPersistQuality);
+ pw.print(" saveDelayMillis:");
+ pw.println(mSaveDelayMillis);
+ pw.print(" resetInterval:");
+ pw.println(mResetInterval);
+ pw.print(" maxUpdatesPerInterval:");
+ pw.println(mMaxUpdatesPerInterval);
+ pw.print(" maxDynamicShortcuts:");
+ pw.println(mMaxDynamicShortcuts);
pw.println();
pw.println(" Stats:");
synchronized (mStatLock) {
- final String p = " ";
+ final String p = " ";
dumpStatLS(pw, p, Stats.GET_DEFAULT_HOME, "getHomeActivities()");
dumpStatLS(pw, p, Stats.LAUNCHER_PERMISSION_CHECK, "Launcher permission check");
@@ -2144,6 +2182,9 @@
case "reset-throttling":
handleResetThrottling();
break;
+ case "reset-all-throttling":
+ handleResetAllThrottling();
+ break;
case "override-config":
handleOverrideConfig();
break;
@@ -2162,6 +2203,9 @@
case "unload-user":
handleUnloadUser();
break;
+ case "clear-shortcuts":
+ handleClearShortcuts();
+ break;
default:
return handleDefaultCommands(cmd);
}
@@ -2181,9 +2225,12 @@
pw.println("cmd shortcut reset-package-throttling [--user USER_ID] PACKAGE");
pw.println(" Reset throttling for a package");
pw.println();
- pw.println("cmd shortcut reset-throttling");
+ pw.println("cmd shortcut reset-throttling [--user USER_ID]");
pw.println(" Reset throttling for all packages and users");
pw.println();
+ pw.println("cmd shortcut reset-all-throttling");
+ pw.println(" Reset the throttling state for all users");
+ pw.println();
pw.println("cmd shortcut override-config CONFIG");
pw.println(" Override the configuration for testing (will last until reboot)");
pw.println();
@@ -2203,13 +2250,23 @@
pw.println(" Unload a user from the memory");
pw.println(" (This should not affect any observable behavior)");
pw.println();
+ pw.println("cmd shortcut clear-shortcuts [--user USER_ID] PACKAGE");
+ pw.println(" Remove all shortcuts from a package, including pinned shortcuts");
+ pw.println();
}
- private int handleResetThrottling() throws CommandException {
+ private void handleResetThrottling() throws CommandException {
parseOptions(/* takeUser =*/ true);
+ Slog.i(TAG, "cmd: handleResetThrottling");
+
resetThrottlingInner(mUserId);
- return 0;
+ }
+
+ private void handleResetAllThrottling() {
+ Slog.i(TAG, "cmd: handleResetAllThrottling");
+
+ resetAllThrottlingInner();
}
private void handleResetPackageThrottling() throws CommandException {
@@ -2217,6 +2274,8 @@
final String packageName = getNextArgRequired();
+ Slog.i(TAG, "cmd: handleResetPackageThrottling: " + packageName);
+
synchronized (mLock) {
getPackageShortcutsLocked(packageName, mUserId).resetRateLimitingForCommandLine();
saveUserLocked(mUserId);
@@ -2226,6 +2285,8 @@
private void handleOverrideConfig() throws CommandException {
final String config = getNextArgRequired();
+ Slog.i(TAG, "cmd: handleOverrideConfig: " + config);
+
synchronized (mLock) {
if (!updateConfigurationLocked(config)) {
throw new CommandException("override-config failed. See logcat for details.");
@@ -2234,6 +2295,8 @@
}
private void handleResetConfig() {
+ Slog.i(TAG, "cmd: handleResetConfig");
+
synchronized (mLock) {
loadConfigurationLocked();
}
@@ -2278,8 +2341,20 @@
private void handleUnloadUser() throws CommandException {
parseOptions(/* takeUser =*/ true);
+ Slog.i(TAG, "cmd: handleUnloadUser: " + mUserId);
+
ShortcutService.this.handleCleanupUser(mUserId);
}
+
+ private void handleClearShortcuts() throws CommandException {
+ parseOptions(/* takeUser =*/ true);
+ final String packageName = getNextArgRequired();
+
+ Slog.i(TAG, "cmd: handleClearShortcuts: " + mUserId + ", " + packageName);
+
+ ShortcutService.this.handlePackageRemovedInner(packageName, mUserId,
+ /* forceForCommandLine= */ true);
+ }
}
// === Unit test support ===
@@ -2357,8 +2432,8 @@
}
@VisibleForTesting
- int getMaxDailyUpdatesForTest() {
- return mMaxDailyUpdates;
+ int getMaxUpdatesPerIntervalForTest() {
+ return mMaxUpdatesPerInterval;
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 60a0d62..60ea254 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -65,6 +65,7 @@
import android.system.Os;
import android.system.OsConstants;
import android.util.AtomicFile;
+import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -483,13 +484,50 @@
}
}
+ @Override
+ public int[] getProfileIds(int userId, boolean enabledOnly) {
+ if (userId != UserHandle.getCallingUserId()) {
+ checkManageUsersPermission("getting profiles related to user " + userId);
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mUsersLock) {
+ return getProfileIdsLU(userId, enabledOnly).toArray();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
/** Assume permissions already checked and caller's identity cleared */
private List<UserInfo> getProfilesLU(int userId, boolean enabledOnly, boolean fullInfo) {
+ IntArray profileIds = getProfileIdsLU(userId, enabledOnly);
+ ArrayList<UserInfo> users = new ArrayList<>(profileIds.size());
+ for (int i = 0; i < profileIds.size(); i++) {
+ int profileId = profileIds.get(i);
+ UserInfo userInfo = mUsers.get(profileId).info;
+ // If full info is not required - clear PII data to prevent 3P apps from reading it
+ if (!fullInfo) {
+ userInfo = new UserInfo(userInfo);
+ userInfo.name = null;
+ userInfo.iconPath = null;
+ } else {
+ userInfo = userWithName(userInfo);
+ }
+ users.add(userInfo);
+ }
+ return users;
+ }
+
+ /**
+ * Assume permissions already checked and caller's identity cleared
+ */
+ private IntArray getProfileIdsLU(int userId, boolean enabledOnly) {
UserInfo user = getUserInfoLU(userId);
- ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+ IntArray result = new IntArray(mUsers.size());
if (user == null) {
// Probably a dying user
- return users;
+ return result;
}
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
@@ -506,16 +544,9 @@
if (profile.partial) {
continue;
}
- UserInfo userInfo = userWithName(profile);
- // If full info is not required - clear PII data to prevent 3P apps from reading it
- if (!fullInfo) {
- userInfo = new UserInfo(userInfo);
- userInfo.name = null;
- userInfo.iconPath = null;
- }
- users.add(userInfo);
+ result.add(profile.id);
}
- return users;
+ return result;
}
@Override
diff --git a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
index 160d44c..27077f2 100644
--- a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
@@ -27,8 +27,11 @@
import android.graphics.drawable.ColorDrawable;
import android.os.Handler;
import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.vr.IVrManager;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -45,6 +48,7 @@
import android.widget.FrameLayout;
import com.android.internal.R;
+import com.android.server.vr.VrManagerService;
/**
* Helper to manage showing/hiding a confirmation prompt when the navigation bar is hidden
@@ -66,6 +70,7 @@
private long mPanicTime;
private WindowManager mWindowManager;
private int mCurrentUserId;
+ private IVrManager mVrManager;
public ImmersiveModeConfirmation(Context context) {
mContext = context;
@@ -75,6 +80,8 @@
.getInteger(R.integer.config_immersive_mode_confirmation_panic);
mWindowManager = (WindowManager)
mContext.getSystemService(Context.WINDOW_SERVICE);
+ mVrManager = (IVrManager) IVrManager.Stub.asInterface(
+ ServiceManager.getService(VrManagerService.VR_MANAGER_BINDER_SERVICE));
}
private long getNavBarExitDuration() {
@@ -112,6 +119,14 @@
}
}
+ private boolean getVrMode() {
+ boolean vrMode = false;
+ try {
+ vrMode = mVrManager.getVrModeState();
+ } catch (RemoteException ex) { }
+ return vrMode;
+ }
+
public void immersiveModeChanged(String pkg, boolean isImmersiveMode,
boolean userSetupComplete) {
mHandler.removeMessages(H.SHOW);
@@ -119,7 +134,10 @@
final boolean disabled = PolicyControl.disableImmersiveConfirmation(pkg);
if (DEBUG) Slog.d(TAG, String.format("immersiveModeChanged() disabled=%s mConfirmed=%s",
disabled, mConfirmed));
- if (!disabled && (DEBUG_SHOW_EVERY_TIME || !mConfirmed) && userSetupComplete) {
+ if (!disabled
+ && (DEBUG_SHOW_EVERY_TIME || !mConfirmed)
+ && userSetupComplete
+ && !getVrMode()) {
mHandler.sendEmptyMessageDelayed(H.SHOW, mShowDelayMs);
}
} else {
diff --git a/services/core/java/com/android/server/policy/ShortcutManager.java b/services/core/java/com/android/server/policy/ShortcutManager.java
index a14c614..ab404db 100644
--- a/services/core/java/com/android/server/policy/ShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ShortcutManager.java
@@ -27,7 +27,9 @@
import android.util.SparseArray;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
+
import com.android.internal.util.XmlUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
index eb926c1..30194bf 100644
--- a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
+++ b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
@@ -24,7 +24,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
@@ -213,18 +212,7 @@
if (userManager == null) {
return null;
}
- int currentUserId = ActivityManager.getCurrentUser();
- List<UserInfo> profiles = userManager.getProfiles(currentUserId);
- if (profiles == null) {
- return null;
- }
- final int s = profiles.size();
- int[] userIds = new int[s];
- int ctr = 0;
- for (UserInfo info : profiles) {
- userIds[ctr++] = info.id;
- }
- return userIds;
+ return userManager.getProfileIdsWithDisabled(ActivityManager.getCurrentUser());
}
public static ArraySet<ComponentName> loadComponentNames(PackageManager pm, int userId,
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index f004b45..49ff385 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -50,6 +50,8 @@
import com.android.server.utils.ManagedApplicationService;
import com.android.server.utils.ManagedApplicationService.BinderChecker;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.lang.StringBuilder;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -103,6 +105,7 @@
new RemoteCallbackList<>();
private final ArraySet<String> mPreviousToggledListenerSettings = new ArraySet<>();
private String mPreviousNotificationPolicyAccessPackage;
+ private String mPreviousCoarseLocationPackage;
private String mPreviousManageOverlayPackage;
private static final int MSG_VR_STATE_CHANGE = 0;
@@ -186,6 +189,32 @@
return VrManagerService.this.getVrMode();
}
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("permission denied: can't dump VrManagerService from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+ pw.print("mVrModeEnabled=");
+ pw.println(mVrModeEnabled);
+ pw.print("mCurrentVrModeUser=");
+ pw.println(mCurrentVrModeUser);
+ pw.print("mRemoteCallbacks=");
+ int i=mRemoteCallbacks.beginBroadcast(); // create the broadcast item array
+ while(i-->0) {
+ pw.print(mRemoteCallbacks.getBroadcastItem(i));
+ if (i>0) pw.print(", ");
+ }
+ mRemoteCallbacks.finishBroadcast();
+ pw.println();
+ pw.print("mCurrentVrService=");
+ pw.println(mCurrentVrService != null ? mCurrentVrService.getComponent() : "(none)");
+ pw.print("mCurrentVrModeComponent=");
+ pw.println(mCurrentVrModeComponent);
+ }
+
};
private void enforceCallerPermission(String permission) {
@@ -418,6 +447,7 @@
mWasDefaultGranted = true;
+ grantCoarseLocationAccess(pName, userId);
grantOverlayAccess(pName, userId);
grantNotificationPolicyAccess(pName);
grantNotificationListenerAccess(pName, userId);
@@ -447,6 +477,7 @@
String pName = component.getPackageName();
if (mWasDefaultGranted) {
+ revokeCoarseLocationAccess(userId);
revokeOverlayAccess(userId);
revokeNotificationPolicyAccess(pName);
revokeNotificiationListenerAccess();
@@ -455,6 +486,27 @@
}
+ private void grantCoarseLocationAccess(String pkg, UserHandle userId) {
+ PackageManager pm = mContext.getPackageManager();
+ boolean prev = (PackageManager.PERMISSION_GRANTED ==
+ pm.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, pkg));
+ mPreviousCoarseLocationPackage = null;
+ if (!prev) {
+ pm.grantRuntimePermission(pkg, android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ userId);
+ mPreviousCoarseLocationPackage = pkg;
+ }
+ }
+
+ private void revokeCoarseLocationAccess(UserHandle userId) {
+ PackageManager pm = mContext.getPackageManager();
+ if (mPreviousCoarseLocationPackage != null) {
+ pm.revokeRuntimePermission(mPreviousCoarseLocationPackage,
+ android.Manifest.permission.ACCESS_COARSE_LOCATION, userId);
+ mPreviousCoarseLocationPackage = null;
+ }
+ }
+
private void grantOverlayAccess(String pkg, UserHandle userId) {
PackageManager pm = mContext.getPackageManager();
boolean prev = (PackageManager.PERMISSION_GRANTED ==
@@ -476,7 +528,6 @@
}
}
-
private void grantNotificationPolicyAccess(String pkg) {
NotificationManager nm = mContext.getSystemService(NotificationManager.class);
boolean prev = nm.isNotificationPolicyAccessGrantedForPackage(pkg);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index fb3c6ec..ca1a7ac 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -16,9 +16,12 @@
package com.android.server.wallpaper;
-import static android.app.WallpaperManager.FLAG_SYSTEM;
import static android.app.WallpaperManager.FLAG_LOCK;
-import static android.os.ParcelFileDescriptor.*;
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+import static android.os.ParcelFileDescriptor.MODE_CREATE;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
@@ -41,9 +44,9 @@
import android.content.ServiceConnection;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -54,13 +57,13 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
+import android.os.FileObserver;
import android.os.FileUtils;
import android.os.IBinder;
import android.os.IRemoteCallback;
-import android.os.RemoteException;
-import android.os.FileObserver;
import android.os.ParcelFileDescriptor;
import android.os.RemoteCallbackList;
+import android.os.RemoteException;
import android.os.SELinux;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -78,35 +81,62 @@
import android.view.IWindowManager;
import android.view.WindowManager;
-import java.io.BufferedOutputStream;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.List;
+import com.android.internal.R;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.JournaledFile;
+import com.android.server.EventLogTags;
+import com.android.server.SystemService;
+
+import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.JournaledFile;
-import com.android.internal.R;
-import com.android.server.EventLogTags;
-
-import libcore.io.IoUtils;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
public class WallpaperManagerService extends IWallpaperManager.Stub {
static final String TAG = "WallpaperManagerService";
static final boolean DEBUG = false;
+ public static class Lifecycle extends SystemService {
+ private WallpaperManagerService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mService = new WallpaperManagerService(getContext());
+ publishBinderService(Context.WALLPAPER_SERVICE, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ mService.systemReady();
+ }
+ }
+
+ @Override
+ public void onUnlockUser(int userHandle) {
+ mService.onUnlockUser(userHandle);
+ }
+ }
+
final Object mLock = new Object();
/**
@@ -265,70 +295,134 @@
*/
private void generateCrop(WallpaperData wallpaper) {
boolean success = false;
- boolean needCrop = false;
- boolean needScale = false;
+
+ Rect cropHint = new Rect(wallpaper.cropHint);
if (DEBUG) {
Slog.v(TAG, "Generating crop for new wallpaper(s): 0x"
+ Integer.toHexString(wallpaper.whichPending)
- + " to " + wallpaper.cropFile.getName());
+ + " to " + wallpaper.cropFile.getName()
+ + " crop=(" + cropHint.width() + 'x' + cropHint.height()
+ + ") dim=(" + wallpaper.width + 'x' + wallpaper.height + ')');
}
// Analyse the source; needed in multiple cases
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options);
-
- // We'll need to scale if the crop is sufficiently bigger than the display
-
- // Legacy case uses an empty crop rect here, so we just preserve the
- // source image verbatim
- if (!wallpaper.cropHint.isEmpty()) {
- // ...clamp the crop rect to the measured bounds...
- wallpaper.cropHint.right = Math.min(wallpaper.cropHint.right, options.outWidth);
- wallpaper.cropHint.bottom = Math.min(wallpaper.cropHint.bottom, options.outHeight);
- // ...and don't bother cropping if what we're left with is identity
- needCrop = (options.outHeight >= wallpaper.cropHint.height()
- && options.outWidth >= wallpaper.cropHint.width());
- }
-
- if (!needCrop && !needScale) {
- // Simple case: the nominal crop is at least as big as the source image,
- // so we take the whole thing and just copy the image file directly.
- if (DEBUG) {
- Slog.v(TAG, "Null crop of new wallpaper; copying");
- }
- success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
- if (!success) {
- wallpaper.cropFile.delete();
- // TODO: fall back to default wallpaper in this case
- }
+ if (options.outWidth <= 0 || options.outHeight <= 0) {
+ Slog.e(TAG, "Invalid wallpaper data");
+ success = false;
} else {
- // Fancy case: crop and/or scale
- FileOutputStream f = null;
- BufferedOutputStream bos = null;
- try {
- BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
- wallpaper.wallpaperFile.getAbsolutePath(), false);
- Bitmap cropped = decoder.decodeRegion(wallpaper.cropHint, null);
- decoder.recycle();
+ boolean needCrop = false;
+ boolean needScale = false;
- if (cropped == null) {
- Slog.e(TAG, "Could not decode new wallpaper");
- } else {
- f = new FileOutputStream(wallpaper.cropFile);
- bos = new BufferedOutputStream(f, 32*1024);
- cropped.compress(Bitmap.CompressFormat.PNG, 90, bos);
- bos.flush(); // don't rely on the implicit flush-at-close when noting success
- success = true;
- }
- } catch (IOException e) {
+ // Empty crop means use the full image
+ if (cropHint.isEmpty()) {
+ cropHint.left = cropHint.top = 0;
+ cropHint.right = options.outWidth;
+ cropHint.bottom = options.outHeight;
+ } else {
+ // force the crop rect to lie within the measured bounds
+ cropHint.offset(
+ (cropHint.right > options.outWidth ? options.outWidth - cropHint.right : 0),
+ (cropHint.bottom > options.outHeight ? options.outHeight - cropHint.bottom : 0));
+
+ // Don't bother cropping if what we're left with is identity
+ needCrop = (options.outHeight >= cropHint.height()
+ && options.outWidth >= cropHint.width());
+ }
+
+ // scale if the crop height winds up not matching the recommended metrics
+ needScale = (wallpaper.height != cropHint.height());
+
+ if (DEBUG) {
+ Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
+ Slog.v(TAG, "dims: w=" + wallpaper.width + " h=" + wallpaper.height);
+ Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
+ Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
+ }
+
+ if (!needCrop && !needScale) {
+ // Simple case: the nominal crop fits what we want, so we take
+ // the whole thing and just copy the image file directly.
if (DEBUG) {
- Slog.e(TAG, "I/O error decoding crop: " + e.getMessage());
+ Slog.v(TAG, "Null crop of new wallpaper; copying");
}
- } finally {
- IoUtils.closeQuietly(bos);
- IoUtils.closeQuietly(f);
+ success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
+ if (!success) {
+ wallpaper.cropFile.delete();
+ // TODO: fall back to default wallpaper in this case
+ }
+ } else {
+ // Fancy case: crop and scale. First, we decode and scale down if appropriate.
+ FileOutputStream f = null;
+ BufferedOutputStream bos = null;
+ try {
+ BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
+ wallpaper.wallpaperFile.getAbsolutePath(), false);
+
+ // This actually downsamples only by powers of two, but that's okay; we do
+ // a proper scaling blit later. This is to minimize transient RAM use.
+ // We calculate the largest power-of-two under the actual ratio rather than
+ // just let the decode take care of it because we also want to remap where the
+ // cropHint rectangle lies in the decoded [super]rect.
+ final BitmapFactory.Options scaler;
+ final int actualScale = cropHint.height() / wallpaper.height;
+ int scale = 1;
+ while (2*scale < actualScale) {
+ scale *= 2;
+ }
+ if (scale > 1) {
+ scaler = new BitmapFactory.Options();
+ scaler.inSampleSize = scale;
+ if (DEBUG) {
+ Slog.v(TAG, "Downsampling cropped rect with scale " + scale);
+ }
+ } else {
+ scaler = null;
+ }
+ Bitmap cropped = decoder.decodeRegion(cropHint, scaler);
+ decoder.recycle();
+
+ if (cropped == null) {
+ Slog.e(TAG, "Could not decode new wallpaper");
+ } else {
+ // We've got the extracted crop; now we want to scale it properly to
+ // the desired rectangle. That's a height-biased operation: make it
+ // fit the hinted height, and accept whatever width we end up with.
+ cropHint.offsetTo(0, 0);
+ cropHint.right /= scale; // adjust by downsampling factor
+ cropHint.bottom /= scale;
+ final float heightR = ((float)wallpaper.height) / ((float)cropHint.height());
+ if (DEBUG) {
+ Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint);
+ }
+ final int destWidth = (int)(cropHint.width() * heightR);
+ final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
+ destWidth, wallpaper.height, true);
+ if (DEBUG) {
+ Slog.v(TAG, "Final extract:");
+ Slog.v(TAG, " dims: w=" + wallpaper.width
+ + " h=" + wallpaper.height);
+ Slog.v(TAG, " out: w=" + finalCrop.getWidth()
+ + " h=" + finalCrop.getHeight());
+ }
+
+ f = new FileOutputStream(wallpaper.cropFile);
+ bos = new BufferedOutputStream(f, 32*1024);
+ finalCrop.compress(Bitmap.CompressFormat.PNG, 90, bos);
+ bos.flush(); // don't rely on the implicit flush-at-close when noting success
+ success = true;
+ }
+ } catch (Exception e) {
+ if (DEBUG) {
+ Slog.e(TAG, "Error decoding crop", e);
+ }
+ } finally {
+ IoUtils.closeQuietly(bos);
+ IoUtils.closeQuietly(f);
+ }
}
}
@@ -352,6 +446,7 @@
final AppOpsManager mAppOpsManager;
WallpaperData mLastWallpaper;
IWallpaperManagerCallback mKeyguardListener;
+ boolean mWaitingForUnlock;
/**
* ID of the current wallpaper, changed every time anything sets a wallpaper.
@@ -679,8 +774,9 @@
if (wallpaper.wallpaperComponent != null
&& isPackageModified(wallpaper.wallpaperComponent.getPackageName())) {
try {
- mContext.getPackageManager().getServiceInfo(
- wallpaper.wallpaperComponent, 0);
+ mContext.getPackageManager().getServiceInfo(wallpaper.wallpaperComponent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
} catch (NameNotFoundException e) {
Slog.w(TAG, "Wallpaper component gone, removing: "
+ wallpaper.wallpaperComponent);
@@ -690,8 +786,9 @@
if (wallpaper.nextWallpaperComponent != null
&& isPackageModified(wallpaper.nextWallpaperComponent.getPackageName())) {
try {
- mContext.getPackageManager().getServiceInfo(
- wallpaper.nextWallpaperComponent, 0);
+ mContext.getPackageManager().getServiceInfo(wallpaper.nextWallpaperComponent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
} catch (NameNotFoundException e) {
wallpaper.nextWallpaperComponent = null;
}
@@ -728,7 +825,7 @@
}
}
- public void systemRunning() {
+ void systemReady() {
if (DEBUG) Slog.v(TAG, "systemReady");
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
// If we think we're going to be using the system image wallpaper imagery, make
@@ -763,7 +860,7 @@
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
+ final String action = intent.getAction();
if (Intent.ACTION_USER_REMOVED.equals(action)) {
onRemoveUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
UserHandle.USER_NULL));
@@ -827,6 +924,14 @@
mLockWallpaperMap.remove(userId);
}
+ void onUnlockUser(int userId) {
+ synchronized (mLock) {
+ if (mCurrentUserId == userId && mWaitingForUnlock) {
+ switchUser(userId, null);
+ }
+ }
+ }
+
void onRemoveUser(int userId) {
if (userId < 1) return;
@@ -854,18 +959,34 @@
void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
synchronized (mLock) {
- RuntimeException e = null;
- try {
- ComponentName cname = wallpaper.wallpaperComponent != null ?
- wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
- if (bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
- return;
+ mWaitingForUnlock = false;
+ final ComponentName cname = wallpaper.wallpaperComponent != null ?
+ wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
+ if (!bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
+ // We failed to bind the desired wallpaper, but that might
+ // happen if the wallpaper isn't direct-boot aware
+ ServiceInfo si = null;
+ try {
+ si = mIPackageManager.getServiceInfo(cname,
+ PackageManager.MATCH_DIRECT_BOOT_UNAWARE, wallpaper.userId);
+ } catch (RemoteException ignored) {
}
- } catch (RuntimeException e1) {
- e = e1;
+
+ if (si == null) {
+ Slog.w(TAG, "Failure starting previous wallpaper; clearing");
+ clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
+ } else {
+ Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
+ // We might end up persisting the current wallpaper data
+ // while locked, so pretend like the component was actually
+ // bound into place
+ wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
+ final WallpaperData fallback = new WallpaperData(wallpaper.userId,
+ WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
+ bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
+ mWaitingForUnlock = true;
+ }
}
- Slog.w(TAG, "Failure starting previous wallpaper", e);
- clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
}
}
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 9486cfd..a5d68da 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -19,6 +19,7 @@
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -33,7 +34,6 @@
import android.provider.Settings;
import android.util.AndroidRuntimeException;
import android.util.Log;
-import android.webkit.WebViewFactory.MissingWebViewPackageException;
import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
@@ -67,6 +67,7 @@
@Override
public WebViewProviderInfo[] getWebViewPackages() {
int numFallbackPackages = 0;
+ int numAvailableByDefaultPackages = 0;
XmlResourceParser parser = null;
List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
try {
@@ -82,12 +83,12 @@
if (element.equals(TAG_WEBVIEW_PROVIDER)) {
String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME);
if (packageName == null) {
- throw new MissingWebViewPackageException(
+ throw new AndroidRuntimeException(
"WebView provider in framework resources missing package name");
}
String description = parser.getAttributeValue(null, TAG_DESCRIPTION);
if (description == null) {
- throw new MissingWebViewPackageException(
+ throw new AndroidRuntimeException(
"WebView provider in framework resources missing description");
}
boolean availableByDefault = "true".equals(
@@ -99,22 +100,33 @@
readSignatures(parser));
if (currentProvider.isFallback) {
numFallbackPackages++;
+ if (!currentProvider.availableByDefault) {
+ throw new AndroidRuntimeException(
+ "Each WebView fallback package must be available by default.");
+ }
if (numFallbackPackages > 1) {
throw new AndroidRuntimeException(
- "There can be at most one webview fallback package.");
+ "There can be at most one WebView fallback package.");
}
}
+ if (currentProvider.availableByDefault) {
+ numAvailableByDefaultPackages++;
+ }
webViewProviders.add(currentProvider);
}
else {
- Log.e(TAG, "Found an element that is not a webview provider");
+ Log.e(TAG, "Found an element that is not a WebView provider");
}
}
} catch (XmlPullParserException | IOException e) {
- throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
+ throw new AndroidRuntimeException("Error when parsing WebView config " + e);
} finally {
if (parser != null) parser.close();
}
+ if (numAvailableByDefaultPackages == 0) {
+ throw new AndroidRuntimeException("There must be at least one WebView package "
+ + "that is available by default");
+ }
return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
}
@@ -187,7 +199,8 @@
enablePackageForAllUsers(context, packageName, false);
try {
PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
- if (pm.getApplicationInfo(packageName, 0).isUpdatedSystemApp()) {
+ ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName, 0);
+ if (applicationInfo != null && applicationInfo.isUpdatedSystemApp()) {
pm.deletePackage(packageName, new IPackageDeleteObserver.Stub() {
public void packageDeleted(String packageName, int returnCode) {
enablePackageForAllUsers(context, packageName, false);
@@ -214,8 +227,9 @@
enable ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT :
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0,
userId, null);
- } catch (RemoteException e) {
- Log.w(TAG, "Tried to disable " + packageName + " for user " + userId + ": " + e);
+ } catch (RemoteException | IllegalArgumentException e) {
+ Log.w(TAG, "Tried to " + (enable ? "enable " : "disable ") + packageName
+ + " for user " + userId + ": " + e);
}
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 1f6fb2a..bbb4951 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -242,7 +242,12 @@
throw new SecurityException(msg);
}
- WebViewUpdateService.this.mImpl.enableFallbackLogic(enable);
+ long callingId = Binder.clearCallingIdentity();
+ try {
+ WebViewUpdateService.this.mImpl.enableFallbackLogic(enable);
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 32b195b..df5d027 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -119,6 +119,8 @@
}
private void updateFallbackStateOnBoot() {
+ if (!mSystemInterface.isFallbackLogicEnabled()) return;
+
WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
updateFallbackState(webviewProviders, true);
}
@@ -160,6 +162,8 @@
isFallbackEnabled = isEnabledPackage(
mSystemInterface.getPackageInfoForProvider(fallbackProvider));
} catch (NameNotFoundException e) {
+ // No fallback package installed -> early out.
+ return;
}
if (existsValidNonFallbackProvider
@@ -495,8 +499,15 @@
mWebViewPackageDirty = false;
// If we have changed provider since we started the relro creation we need to
// redo the whole process using the new package instead.
- PackageInfo newPackage = findPreferredWebViewPackage();
- onWebViewProviderChanged(newPackage);
+ try {
+ PackageInfo newPackage = findPreferredWebViewPackage();
+ onWebViewProviderChanged(newPackage);
+ } catch (WebViewFactory.MissingWebViewPackageException e) {
+ // If we can't find any valid WebView package we are now in a state where
+ // mAnyWebViewInstalled is false, so loading WebView will be blocked and we
+ // should simply wait until we receive an intent declaring a new package was
+ // installed.
+ }
} else {
mLock.notifyAll();
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 28379f4..1f16481 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -290,7 +290,7 @@
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
TaskStack stack = mStacks.get(stackNdx);
stack.getBounds(mTmpRect);
- if (!mTmpRect.contains(x, y)) {
+ if (!mTmpRect.contains(x, y) || stack.isAdjustedForMinimizedDockedStack()) {
continue;
}
final ArrayList<Task> tasks = stack.getTasks();
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 6ac71c7..ff537be 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -270,6 +270,19 @@
mDockedStackListeners.finishBroadcast();
}
+ void notifyDockSideChanged(int newDockSide) {
+ final int size = mDockedStackListeners.beginBroadcast();
+ for (int i = 0; i < size; ++i) {
+ final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
+ try {
+ listener.onDockSideChanged(newDockSide);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering dock side changed event.", e);
+ }
+ }
+ mDockedStackListeners.finishBroadcast();
+ }
+
void registerDockedStackListener(IDockedStackListener listener) {
mDockedStackListeners.register(listener);
notifyDockedDividerVisibilityChanged(wasVisible());
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index eea0e73..be9fb26 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -294,7 +294,8 @@
final WindowState child = windows.get(winNdx);
final InputChannel inputChannel = child.mInputChannel;
final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
- if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
+ if (inputChannel == null || inputWindowHandle == null || child.mRemoved
+ || child.isAdjustedForMinimizedDock()) {
// Skip this window because it cannot possibly receive input.
continue;
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 446b74b..7074a83 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -16,6 +16,20 @@
package com.android.server.wm;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
+
import android.app.ActivityManager.StackId;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -35,20 +49,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
-import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
public class TaskStack implements DimLayer.DimLayerUser,
BoundsAnimationController.AnimateBoundsUser {
@@ -379,10 +379,15 @@
return false;
}
+ final int oldDockSide = mStackId == DOCKED_STACK_ID ? getDockSide() : DOCKED_INVALID;
mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
if (mStackId == DOCKED_STACK_ID) {
repositionDockedStackAfterRotation(mTmpRect2);
snapDockedStackAfterRotation(mTmpRect2);
+ final int newDockSide = getDockSide(mTmpRect2);
+ if (oldDockSide != newDockSide) {
+ mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
+ }
}
if (scheduleResize) {
@@ -910,7 +915,7 @@
// Calculate the content bounds excluding the area occupied by IME
getDisplayContent().getContentRect(displayContentRect);
contentBounds.set(displayContentRect);
- int imeTop = Math.max(imeWin.getDisplayFrameLw().top, contentBounds.top);
+ int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top);
// if IME window is animating, get its actual vertical shown position (but no smaller than
// the final target vertical position)
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index eae7838..6bc633f 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -670,7 +670,6 @@
if (SHOW_TRANSACTIONS) Slog.i(
TAG, ">>> OPEN TRANSACTION animateLocked");
SurfaceControl.openTransaction();
- SurfaceControl.setAnimationTransaction();
try {
final int numDisplays = mDisplayContentsAnimators.size();
for (int i = 0; i < numDisplays; i++) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c991130..c48d7d1 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -774,10 +774,6 @@
applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame);
- // Offset the actual frame by the amount layout frame is off.
- mFrame.offset(-layoutXDiff, -layoutYDiff);
- mCompatFrame.offset(-layoutXDiff, -layoutYDiff);
-
// Calculate the outsets before the content frame gets shrinked to the window frame.
if (hasOutsets) {
mOutsets.set(Math.max(mContentFrame.left - mOutsetFrame.left, 0),
@@ -816,20 +812,20 @@
mMovedByResize = true;
}
} else {
- mContentFrame.set(Math.max(mContentFrame.left, layoutContainingFrame.left),
- Math.max(mContentFrame.top, layoutContainingFrame.top),
- Math.min(mContentFrame.right, layoutContainingFrame.right),
- Math.min(mContentFrame.bottom, layoutContainingFrame.bottom));
+ mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
+ Math.max(mContentFrame.top, mFrame.top),
+ Math.min(mContentFrame.right, mFrame.right),
+ Math.min(mContentFrame.bottom, mFrame.bottom));
- mVisibleFrame.set(Math.max(mVisibleFrame.left, layoutContainingFrame.left),
- Math.max(mVisibleFrame.top, layoutContainingFrame.top),
- Math.min(mVisibleFrame.right, layoutContainingFrame.right),
- Math.min(mVisibleFrame.bottom, layoutContainingFrame.bottom));
+ mVisibleFrame.set(Math.max(mVisibleFrame.left, mFrame.left),
+ Math.max(mVisibleFrame.top, mFrame.top),
+ Math.min(mVisibleFrame.right, mFrame.right),
+ Math.min(mVisibleFrame.bottom, mFrame.bottom));
- mStableFrame.set(Math.max(mStableFrame.left, layoutContainingFrame.left),
- Math.max(mStableFrame.top, layoutContainingFrame.top),
- Math.min(mStableFrame.right, layoutContainingFrame.right),
- Math.min(mStableFrame.bottom, layoutContainingFrame.bottom));
+ mStableFrame.set(Math.max(mStableFrame.left, mFrame.left),
+ Math.max(mStableFrame.top, mFrame.top),
+ Math.min(mStableFrame.right, mFrame.right),
+ Math.min(mStableFrame.bottom, mFrame.bottom));
}
if (fullscreenTask && !windowsAreFloating) {
@@ -857,30 +853,33 @@
getDisplayContent().getLogicalDisplayRect(mTmpRect);
// Override right and/or bottom insets in case if the frame doesn't fit the screen in
// non-fullscreen mode.
- boolean overrideRightInset = !fullscreenTask && layoutContainingFrame.right > mTmpRect.right;
- boolean overrideBottomInset = !fullscreenTask && layoutContainingFrame.bottom > mTmpRect.bottom;
- mContentInsets.set(mContentFrame.left - layoutContainingFrame.left,
- mContentFrame.top - layoutContainingFrame.top,
+ boolean overrideRightInset = !fullscreenTask && mFrame.right > mTmpRect.right;
+ boolean overrideBottomInset = !fullscreenTask && mFrame.bottom > mTmpRect.bottom;
+ mContentInsets.set(mContentFrame.left - mFrame.left,
+ mContentFrame.top - mFrame.top,
overrideRightInset ? mTmpRect.right - mContentFrame.right
- : layoutContainingFrame.right - mContentFrame.right,
+ : mFrame.right - mContentFrame.right,
overrideBottomInset ? mTmpRect.bottom - mContentFrame.bottom
- : layoutContainingFrame.bottom - mContentFrame.bottom);
+ : mFrame.bottom - mContentFrame.bottom);
- mVisibleInsets.set(mVisibleFrame.left - layoutContainingFrame.left,
- mVisibleFrame.top - layoutContainingFrame.top,
+ mVisibleInsets.set(mVisibleFrame.left - mFrame.left,
+ mVisibleFrame.top - mFrame.top,
overrideRightInset ? mTmpRect.right - mVisibleFrame.right
- : layoutContainingFrame.right - mVisibleFrame.right,
+ : mFrame.right - mVisibleFrame.right,
overrideBottomInset ? mTmpRect.bottom - mVisibleFrame.bottom
- : layoutContainingFrame.bottom - mVisibleFrame.bottom);
+ : mFrame.bottom - mVisibleFrame.bottom);
- mStableInsets.set(Math.max(mStableFrame.left - layoutContainingFrame.left, 0),
- Math.max(mStableFrame.top - layoutContainingFrame.top, 0),
+ mStableInsets.set(Math.max(mStableFrame.left - mFrame.left, 0),
+ Math.max(mStableFrame.top - mFrame.top, 0),
overrideRightInset ? Math.max(mTmpRect.right - mStableFrame.right, 0)
- : Math.max(layoutContainingFrame.right - mStableFrame.right, 0),
+ : Math.max(mFrame.right - mStableFrame.right, 0),
overrideBottomInset ? Math.max(mTmpRect.bottom - mStableFrame.bottom, 0)
- : Math.max(layoutContainingFrame.bottom - mStableFrame.bottom, 0));
+ : Math.max(mFrame.bottom - mStableFrame.bottom, 0));
}
+ // Offset the actual frame by the amount layout frame is off.
+ mFrame.offset(-layoutXDiff, -layoutYDiff);
+ mCompatFrame.offset(-layoutXDiff, -layoutYDiff);
mContentFrame.offset(-layoutXDiff, -layoutYDiff);
mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
mStableFrame.offset(-layoutXDiff, -layoutYDiff);
@@ -1390,6 +1389,11 @@
return configChanged;
}
+ boolean isAdjustedForMinimizedDock() {
+ return mAppToken != null && mAppToken.mTask != null
+ && mAppToken.mTask.mStack.isAdjustedForMinimizedDock();
+ }
+
void removeLocked() {
disposeInputChannel();
@@ -1776,7 +1780,8 @@
return isVisibleOrAdding()
&& (mViewVisibility == View.VISIBLE)
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
- && (mAppToken == null || mAppToken.windowsAreFocusable());
+ && (mAppToken == null || mAppToken.windowsAreFocusable())
+ && !isAdjustedForMinimizedDock();
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9c25f63..140f381 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1288,7 +1288,7 @@
}
final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
- if (w == winShowWhenLocked) {
+ if (w == winShowWhenLocked && mPolicy.isKeyguardShowingOrOccluded()) {
return;
}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 78b0844..93eb82f 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -562,7 +562,7 @@
method_reportNavigationMessages = env->GetMethodID(
clazz,
"reportNavigationMessage",
- "(Landroid/location/GnssNavigationMessageEvent;)V");
+ "(Landroid/location/GnssNavigationMessage;)V");
err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
if (err == 0) {
@@ -1486,26 +1486,6 @@
return object.get();
}
-static void set_navigation_message(jobject navigationMessage) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- jclass navigationMessageEventClass =
- env->FindClass("android/location/GnssNavigationMessageEvent");
- jmethodID navigationMessageEventCtor = env->GetMethodID(
- navigationMessageEventClass,
- "<init>",
- "(Landroid/location/GnssNavigationMessage;)V");
- jobject navigationMessageEvent = env->NewObject(
- navigationMessageEventClass,
- navigationMessageEventCtor,
- navigationMessage);
- env->CallVoidMethod(mCallbacksObj,
- method_reportNavigationMessages,
- navigationMessageEvent);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- env->DeleteLocalRef(navigationMessageEventClass);
- env->DeleteLocalRef(navigationMessageEvent);
-}
-
static void navigation_message_callback(GpsNavigationMessage* message) {
if (message == NULL) {
ALOGE("Invalid Navigation Message provided to callback");
@@ -1517,7 +1497,9 @@
}
JNIEnv* env = AndroidRuntime::getJNIEnv();
jobject navigationMessage = translate_gps_navigation_message(env, message);
- set_navigation_message(navigationMessage);
+ env->CallVoidMethod(mCallbacksObj,
+ method_reportNavigationMessages,
+ navigationMessage);
env->DeleteLocalRef(navigationMessage);
}
@@ -1532,7 +1514,9 @@
}
JNIEnv* env = AndroidRuntime::getJNIEnv();
jobject navigationMessage = translate_gnss_navigation_message(env, message);
- set_navigation_message(navigationMessage);
+ env->CallVoidMethod(mCallbacksObj,
+ method_reportNavigationMessages,
+ navigationMessage);
env->DeleteLocalRef(navigationMessage);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 72eebb5..d229633 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -21,8 +21,6 @@
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import static android.content.pm.PackageManager.GET_UNINSTALLED_PACKAGES;
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -57,7 +55,6 @@
import android.app.backup.IBackupManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -85,7 +82,6 @@
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Build;
-import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
@@ -2065,10 +2061,9 @@
*/
private void sendAdminCommandToSelfAndProfilesLocked(String action, int reqPolicy,
int userHandle) {
- List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
- for (UserInfo ui : profiles) {
- int id = ui.id;
- sendAdminCommandLocked(action, reqPolicy, id);
+ int[] profileIds = mUserManager.getProfileIdsWithDisabled(userHandle);
+ for (int profileId : profileIds) {
+ sendAdminCommandLocked(action, reqPolicy, profileId);
}
}
@@ -3968,9 +3963,9 @@
// moment so we set the screen off timeout regardless of whether it affects the parent user
// or the profile challenge only.
long timeMs = Long.MAX_VALUE;
- List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
- for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
+ int[] profileIds = mUserManager.getProfileIdsWithDisabled(userHandle);
+ for (int profileId : profileIds) {
+ DevicePolicyData policy = getUserDataUnchecked(profileId);
final int N = policy.mAdminList.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -6708,19 +6703,18 @@
// If we have multiple profiles we return the intersection of the
// permitted lists. This can happen in cases where we have a device
// and profile owner.
- List<UserInfo> profiles = mUserManager.getProfiles(userId);
- final int PROFILES_SIZE = profiles.size();
- for (int i = 0; i < PROFILES_SIZE; ++i) {
+ int[] profileIds = mUserManager.getProfileIdsWithDisabled(userId);
+ for (int profileId : profileIds) {
// Just loop though all admins, only device or profiles
// owners can have permitted lists set.
- DevicePolicyData policy = getUserDataUnchecked(profiles.get(i).id);
+ DevicePolicyData policy = getUserDataUnchecked(profileId);
final int N = policy.mAdminList.size();
for (int j = 0; j < N; j++) {
ActiveAdmin admin = policy.mAdminList.get(j);
List<String> fromAdmin = admin.permittedAccessiblityServices;
if (fromAdmin != null) {
if (result == null) {
- result = new ArrayList<String>(fromAdmin);
+ result = new ArrayList<>(fromAdmin);
} else {
result.retainAll(fromAdmin);
}
@@ -6888,12 +6882,11 @@
// If we have multiple profiles we return the intersection of the
// permitted lists. This can happen in cases where we have a device
// and profile owner.
- List<UserInfo> profiles = mUserManager.getProfiles(userId);
- final int PROFILES_SIZE = profiles.size();
- for (int i = 0; i < PROFILES_SIZE; ++i) {
+ int[] profileIds = mUserManager.getProfileIdsWithDisabled(userId);
+ for (int profileId : profileIds) {
// Just loop though all admins, only device or profiles
// owners can have permitted lists set.
- DevicePolicyData policy = getUserDataUnchecked(profiles.get(i).id);
+ DevicePolicyData policy = getUserDataUnchecked(profileId);
final int N = policy.mAdminList.size();
for (int j = 0; j < N; j++) {
ActiveAdmin admin = policy.mAdminList.get(j);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e7ae2b0..8c2f559 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -18,7 +18,6 @@
import android.app.ActivityManagerNative;
import android.app.ActivityThread;
-import android.app.IAlarmManager;
import android.app.INotificationManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
@@ -55,13 +54,11 @@
import com.android.internal.os.ZygoteInit;
import com.android.internal.widget.ILockSettings;
import com.android.server.accessibility.AccessibilityManagerService;
-import com.android.server.accounts.AccountManagerService;
import com.android.server.am.ActivityManagerService;
import com.android.server.audio.AudioService;
import com.android.server.camera.CameraService;
import com.android.server.clipboard.ClipboardService;
import com.android.server.connectivity.MetricsLoggerService;
-import com.android.server.content.ContentService;
import com.android.server.devicepolicy.DevicePolicyManagerService;
import com.android.server.display.DisplayManagerService;
import com.android.server.dreams.DreamManagerService;
@@ -97,7 +94,6 @@
import com.android.server.twilight.TwilightService;
import com.android.server.usage.UsageStatsService;
import com.android.server.vr.VrManagerService;
-import com.android.server.wallpaper.WallpaperManagerService;
import com.android.server.webkit.WebViewUpdateService;
import com.android.server.wm.WindowManagerService;
@@ -157,8 +153,12 @@
"com.google.android.clockwork.ThermalObserver";
private static final String WEAR_BLUETOOTH_SERVICE_CLASS =
"com.google.android.clockwork.bluetooth.WearBluetoothService";
+ private static final String ACCOUNT_SERVICE_CLASS =
+ "com.android.server.accounts.AccountManagerService$Lifecycle";
private static final String CONTENT_SERVICE_CLASS =
"com.android.server.content.ContentService$Lifecycle";
+ private static final String WALLPAPER_SERVICE_CLASS =
+ "com.android.server.wallpaper.WallpaperManagerService$Lifecycle";
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
@@ -187,6 +187,7 @@
private PackageManagerService mPackageManagerService;
private PackageManager mPackageManager;
private ContentResolver mContentResolver;
+ private EntropyMixer mEntropyMixer;
private boolean mOnlyCore;
private boolean mFirstBoot;
@@ -497,10 +498,7 @@
*/
private void startOtherServices() {
final Context context = mSystemContext;
- AccountManagerService accountManager = null;
- ContentService contentService = null;
VibratorService vibrator = null;
- IAlarmManager alarm = null;
IMountService mountService = null;
NetworkManagementService networkManagement = null;
NetworkStatsService networkStats = null;
@@ -516,8 +514,6 @@
TelephonyRegistry telephonyRegistry = null;
ConsumerIrService consumerIr = null;
MmsServiceBroker mmsService = null;
- EntropyMixer entropyMixer = null;
- VrManagerService vrManagerService = null;
HardwarePropertiesManagerService hardwarePropertiesService = null;
boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
@@ -556,7 +552,7 @@
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
traceBeginAndSlog("StartEntropyMixer");
- entropyMixer = new EntropyMixer(context);
+ mEntropyMixer = new EntropyMixer(context);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
mContentResolver = context.getContentResolver();
@@ -566,13 +562,7 @@
// The AccountManager must come before the ContentService
traceBeginAndSlog("StartAccountManagerService");
- try {
- // TODO: seems like this should be disable-able, but req'd by ContentService
- accountManager = new AccountManagerService(context);
- ServiceManager.addService(Context.ACCOUNT_SERVICE, accountManager);
- } catch (Throwable e) {
- Slog.e(TAG, "Failure starting Account Manager", e);
- }
+ mSystemServiceManager.startService(ACCOUNT_SERVICE_CLASS);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
traceBeginAndSlog("StartContentService");
@@ -593,9 +583,9 @@
ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ traceBeginAndSlog("StartAlarmManagerService");
mSystemServiceManager.startService(AlarmManagerService.class);
- alarm = IAlarmManager.Stub.asInterface(
- ServiceManager.getService(Context.ALARM_SERVICE));
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
traceBeginAndSlog("InitWatchdog");
final Watchdog watchdog = Watchdog.getInstance();
@@ -652,7 +642,6 @@
StatusBarManagerService statusBar = null;
INotificationManager notification = null;
- WallpaperManagerService wallpaper = null;
LocationManagerService location = null;
CountryDetectorService countryDetector = null;
ILockSettings lockSettings = null;
@@ -823,7 +812,7 @@
mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
mSystemServiceManager.startService(
- "com.android.server.wifi.WifiScanningService");
+ "com.android.server.wifi.scanner.WifiScanningService");
if (!disableRtt) {
mSystemServiceManager.startService("com.android.server.wifi.RttService");
@@ -886,24 +875,6 @@
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
- Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeAccountManagerServiceReady");
- try {
- if (accountManager != null)
- accountManager.systemReady();
- } catch (Throwable e) {
- reportWtf("making Account Manager Service ready", e);
- }
- Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
-
- Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeContentServiceReady");
- try {
- if (contentService != null)
- contentService.systemReady();
- } catch (Throwable e) {
- reportWtf("making Content Service ready", e);
- }
- Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
-
mSystemServiceManager.startService(NotificationManagerService.class);
notification = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
@@ -946,12 +917,7 @@
if (!disableNonCoreServices && context.getResources().getBoolean(
R.bool.config_enableWallpaperService)) {
traceBeginAndSlog("StartWallpaperManagerService");
- try {
- wallpaper = new WallpaperManagerService(context);
- ServiceManager.addService(Context.WALLPAPER_SERVICE, wallpaper);
- } catch (Throwable e) {
- reportWtf("starting Wallpaper Service", e);
- }
+ mSystemServiceManager.startService(WALLPAPER_SERVICE_CLASS);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
@@ -1278,12 +1244,10 @@
final NetworkPolicyManagerService networkPolicyF = networkPolicy;
final ConnectivityService connectivityF = connectivity;
final NetworkScoreService networkScoreF = networkScore;
- final WallpaperManagerService wallpaperF = wallpaper;
final LocationManagerService locationF = location;
final CountryDetectorService countryDetectorF = countryDetector;
final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
final CommonTimeManagementService commonTimeMgmtServiceF = commonTimeMgmtService;
- final StatusBarManagerService statusBarF = statusBar;
final AssetAtlasService atlasF = atlas;
final InputManagerService inputManagerF = inputManager;
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
@@ -1371,11 +1335,6 @@
SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
try {
- if (wallpaperF != null) wallpaperF.systemRunning();
- } catch (Throwable e) {
- reportWtf("Notifying WallpaperService running", e);
- }
- try {
if (locationF != null) locationF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying Location Service running", e);
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 54aeb3d..b6ad25b 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -355,6 +355,8 @@
public void startProvisioning(ProvisioningConfiguration req) {
getNetworkInterface();
+
+ mCallback.setNeighborDiscoveryOffload(true);
sendMessage(CMD_START, new ProvisioningConfiguration(req));
}
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index e6dd6a4..7ffdb35 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -19,7 +19,8 @@
easymocklib \
guava \
android-support-test \
- mockito-target
+ mockito-target \
+ ShortcutManagerTestUtils
LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 2f20a4b..26eed24 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -70,6 +70,7 @@
import com.android.internal.util.WakeupMessage;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor;
+import com.android.server.connectivity.NetworkMonitor.CaptivePortalProbeResult;
import com.android.server.net.NetworkPinner;
import java.net.InetAddress;
@@ -223,11 +224,15 @@
private final NetworkCapabilities mNetworkCapabilities;
private final IdleableHandlerThread mHandlerThread;
private final ConditionVariable mDisconnected = new ConditionVariable();
+ private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
private int mScore;
private NetworkAgent mNetworkAgent;
private int mStartKeepaliveError = PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED;
private int mStopKeepaliveError = PacketKeepalive.NO_KEEPALIVE;
private Integer mExpectedKeepaliveSlot = null;
+ // Contains the redirectUrl from networkStatus(). Before reading, wait for
+ // mNetworkStatusReceived.
+ private String mRedirectUrl;
MockNetworkAgent(int transport) {
final int type = transportToLegacyType(transport);
@@ -266,6 +271,12 @@
public void stopPacketKeepalive(Message msg) {
onPacketKeepaliveEvent(msg.arg1, mStopKeepaliveError);
}
+
+ @Override
+ public void networkStatus(int status, String redirectUrl) {
+ mRedirectUrl = redirectUrl;
+ mNetworkStatusReceived.open();
+ }
};
// Waits for the NetworkAgent to be registered, which includes the creation of the
// NetworkMonitor.
@@ -340,13 +351,15 @@
if (callback != null) mCm.unregisterNetworkCallback(callback);
}
- public void connectWithCaptivePortal() {
+ public void connectWithCaptivePortal(String redirectUrl) {
mWrappedNetworkMonitor.gen204ProbeResult = 200;
+ mWrappedNetworkMonitor.gen204ProbeRedirectUrl = redirectUrl;
connect(false);
waitFor(new Criteria() { public boolean get() {
NetworkCapabilities caps = mCm.getNetworkCapabilities(getNetwork());
return caps != null && caps.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL);} });
mWrappedNetworkMonitor.gen204ProbeResult = 500;
+ mWrappedNetworkMonitor.gen204ProbeRedirectUrl = null;
}
public void disconnect() {
@@ -381,6 +394,11 @@
public void setExpectedKeepaliveSlot(Integer slot) {
mExpectedKeepaliveSlot = slot;
}
+
+ public String waitForRedirectUrl() {
+ assertTrue(mNetworkStatusReceived.block(TIMEOUT_MS));
+ return mRedirectUrl;
+ }
}
/**
@@ -543,6 +561,7 @@
private class WrappedNetworkMonitor extends NetworkMonitor {
// HTTP response code fed back to NetworkMonitor for Internet connectivity probe.
public int gen204ProbeResult = 500;
+ public String gen204ProbeRedirectUrl = null;
public WrappedNetworkMonitor(Context context, Handler handler,
NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest) {
@@ -550,8 +569,8 @@
}
@Override
- protected int isCaptivePortal() {
- return gen204ProbeResult;
+ protected CaptivePortalProbeResult isCaptivePortal() {
+ return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl);
}
@Override
@@ -1344,8 +1363,10 @@
// Bring up a network with a captive portal.
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- mWiFiNetworkAgent.connectWithCaptivePortal();
+ String firstRedirectUrl = "http://example.com/firstPath";
+ mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
captivePortalCallback.expectCallback(CallbackState.AVAILABLE);
+ assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl);
// Take down network.
// Expect onLost callback.
@@ -1355,8 +1376,10 @@
// Bring up a network with a captive portal.
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- mWiFiNetworkAgent.connectWithCaptivePortal();
+ String secondRedirectUrl = "http://example.com/secondPath";
+ mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
captivePortalCallback.expectCallback(CallbackState.AVAILABLE);
+ assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl);
// Make captive portal disappear then revalidate.
// Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL.
diff --git a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
index dae8447..7d28e39 100644
--- a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
@@ -87,6 +87,12 @@
return new File(mStorageDir,
super.getLockPasswordFilename(userId).replace('/', '-')).getAbsolutePath();
}
+
+ @Override
+ String getChildProfileLockFile(int userId) {
+ return new File(mStorageDir,
+ super.getChildProfileLockFile(userId).replace('/', '-')).getAbsolutePath();
+ }
};
}
@@ -235,6 +241,27 @@
assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2).hash);
}
+ public void testLockType_WriteProfileWritesParent() {
+ mStorage.writePasswordHash("parentpassword".getBytes(), 10);
+ mStorage.writePatternHash("12345678".getBytes(), 20);
+
+ assertEquals(2, mStorage.getStoredCredentialType(10));
+ assertEquals(1, mStorage.getStoredCredentialType(20));
+ mStorage.clearCache();
+ assertEquals(2, mStorage.getStoredCredentialType(10));
+ assertEquals(1, mStorage.getStoredCredentialType(20));
+ }
+
+ public void testProfileLock_ReadWriteChildProfileLock() {
+ assertFalse(mStorage.hasChildProfileLock(20));
+ mStorage.writeChildProfileLock(20, "profilepassword".getBytes());
+ assertArrayEquals("profilepassword".getBytes(), mStorage.readChildProfileLock(20));
+ assertTrue(mStorage.hasChildProfileLock(20));
+ mStorage.clearCache();
+ assertArrayEquals("profilepassword".getBytes(), mStorage.readChildProfileLock(20));
+ assertTrue(mStorage.hasChildProfileLock(20));
+ }
+
public void testPassword_WriteParentWritesProfile() {
mStorage.writePasswordHash("profilepassword".getBytes(), 2);
mStorage.writePasswordHash("parentpasswordd".getBytes(), 1);
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 5b4803b..4468857 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -16,23 +16,34 @@
package com.android.server.accounts;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.accounts.Account;
import android.accounts.AuthenticatorDescription;
+import android.app.AppOpsManager;
import android.app.Notification;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.RegisteredServicesCache.ServiceInfo;
import android.content.pm.RegisteredServicesCacheListener;
+import android.content.pm.UserInfo;
+import android.database.DatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
+import android.os.UserManager;
import android.test.AndroidTestCase;
-import android.test.IsolatedContext;
-import android.test.RenamingDelegatingContext;
-import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
import android.test.mock.MockPackageManager;
+import android.util.Log;
+import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -41,20 +52,28 @@
import java.util.Comparator;
public class AccountManagerServiceTest extends AndroidTestCase {
+ private static final String TAG = AccountManagerServiceTest.class.getSimpleName();
+
+ static final String PREN_DB = "pren.db";
+ static final String DE_DB = "de.db";
+ static final String CE_DB = "ce.db";
private AccountManagerService mAms;
@Override
protected void setUp() throws Exception {
- final String filenamePrefix = "test.";
- MockContentResolver resolver = new MockContentResolver();
- RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
- new MyMockContext(), // The context that most methods are delegated to
- getContext(), // The context that file methods are delegated to
- filenamePrefix);
- Context context = new IsolatedContext(resolver, targetContextWrapper);
- setContext(context);
+ Context realTestContext = getContext();
+ Context mockContext = new MyMockContext(realTestContext);
+ setContext(mockContext);
mAms = new MyAccountManagerService(getContext(),
- new MyMockPackageManager(), new MockAccountAuthenticatorCache());
+ new MyMockPackageManager(), new MockAccountAuthenticatorCache(), realTestContext);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ new File(mAms.getCeDatabaseName(UserHandle.USER_SYSTEM)).delete();
+ new File(mAms.getDeDatabaseName(UserHandle.USER_SYSTEM)).delete();
+ new File(mAms.getPreNDatabaseName(UserHandle.USER_SYSTEM)).delete();
+ super.tearDown();
}
public class AccountSorter implements Comparator<Account> {
@@ -69,6 +88,7 @@
}
public void testCheckAddAccount() throws Exception {
+ unlockUser(UserHandle.USER_SYSTEM);
Account a11 = new Account("account1", "type1");
Account a21 = new Account("account2", "type1");
Account a31 = new Account("account3", "type1");
@@ -109,6 +129,7 @@
}
public void testPasswords() throws Exception {
+ unlockUser(UserHandle.USER_SYSTEM);
Account a11 = new Account("account1", "type1");
Account a12 = new Account("account1", "type2");
mAms.addAccountExplicitly(a11, "p11", null);
@@ -124,6 +145,7 @@
}
public void testUserdata() throws Exception {
+ unlockUser(UserHandle.USER_SYSTEM);
Account a11 = new Account("account1", "type1");
Bundle u11 = new Bundle();
u11.putString("a", "a_a11");
@@ -156,6 +178,7 @@
}
public void testAuthtokens() throws Exception {
+ unlockUser(UserHandle.USER_SYSTEM);
Account a11 = new Account("account1", "type1");
Account a12 = new Account("account1", "type2");
mAms.addAccountExplicitly(a11, "p11", null);
@@ -188,15 +211,21 @@
assertNull(mAms.peekAuthToken(a12, "att2"));
}
+ private void unlockUser(int userId) {
+ Intent intent = new Intent();
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mAms.onUserUnlocked(intent);
+ }
+
static public class MockAccountAuthenticatorCache implements IAccountAuthenticatorCache {
private ArrayList<ServiceInfo<AuthenticatorDescription>> mServices;
public MockAccountAuthenticatorCache() {
- mServices = new ArrayList<ServiceInfo<AuthenticatorDescription>>();
+ mServices = new ArrayList<>();
AuthenticatorDescription d1 = new AuthenticatorDescription("type1", "p1", 0, 0, 0, 0);
AuthenticatorDescription d2 = new AuthenticatorDescription("type2", "p2", 0, 0, 0, 0);
- mServices.add(new ServiceInfo<AuthenticatorDescription>(d1, null, null));
- mServices.add(new ServiceInfo<AuthenticatorDescription>(d2, null, null));
+ mServices.add(new ServiceInfo<>(d1, null, null));
+ mServices.add(new ServiceInfo<>(d2, null, null));
}
@Override
@@ -232,10 +261,68 @@
}
static public class MyMockContext extends MockContext {
+ private Context mTestContext;
+ private AppOpsManager mAppOpsManager;
+ private UserManager mUserManager;
+
+ public MyMockContext(Context testContext) {
+ this.mTestContext = testContext;
+ this.mAppOpsManager = mock(AppOpsManager.class);
+ this.mUserManager = mock(UserManager.class);
+ final UserInfo ui = new UserInfo(UserHandle.USER_SYSTEM, "user0", 0);
+ when(mUserManager.getUserInfo(eq(ui.id))).thenReturn(ui);
+ }
+
@Override
public int checkCallingOrSelfPermission(final String permission) {
return PackageManager.PERMISSION_GRANTED;
}
+
+ @Override
+ public Object getSystemService(String name) {
+ if (Context.APP_OPS_SERVICE.equals(name)) {
+ return mAppOpsManager;
+ } else if( Context.USER_SERVICE.equals(name)) {
+ return mUserManager;
+ }
+ return null;
+ }
+
+ @Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ if (AppOpsManager.class.equals(serviceClass)) {
+ return Context.APP_OPS_SERVICE;
+ }
+ return null;
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ return null;
+ }
+
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ return null;
+ }
+
+ @Override
+ public SQLiteDatabase openOrCreateDatabase(String file, int mode,
+ SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {
+ Log.i(TAG, "openOrCreateDatabase " + file + " mode " + mode);
+ return mTestContext.openOrCreateDatabase(file, mode, factory,errorHandler);
+ }
+
+ @Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user) {
+ Log.i(TAG, "sendBroadcastAsUser " + intent + " " + user);
+ }
+
+ @Override
+ public String getOpPackageName() {
+ return null;
+ }
}
static public class MyMockPackageManager extends MockPackageManager {
@@ -246,9 +333,11 @@
}
static public class MyAccountManagerService extends AccountManagerService {
+ private Context mRealTestContext;
public MyAccountManagerService(Context context, PackageManager packageManager,
- IAccountAuthenticatorCache authenticatorCache) {
+ IAccountAuthenticatorCache authenticatorCache, Context realTestContext) {
super(context, packageManager, authenticatorCache);
+ this.mRealTestContext = realTestContext;
}
@Override
@@ -258,5 +347,20 @@
@Override
protected void cancelNotification(final int id, UserHandle user) {
}
+
+ @Override
+ protected String getCeDatabaseName(int userId) {
+ return new File(mRealTestContext.getCacheDir(), CE_DB).getPath();
+ }
+
+ @Override
+ protected String getDeDatabaseName(int userId) {
+ return new File(mRealTestContext.getCacheDir(), DE_DB).getPath();
+ }
+
+ @Override
+ String getPreNDatabaseName(int userId) {
+ return new File(mRealTestContext.getCacheDir(), PREN_DB).getPath();
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 60d7382..f6e35f5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -39,7 +39,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
-import android.os.storage.StorageManager;
import android.telephony.TelephonyManager;
import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
@@ -343,31 +342,25 @@
@Override
public List<UserInfo> answer(InvocationOnMock invocation) throws Throwable {
final int userId = (int) invocation.getArguments()[0];
- final ArrayList<UserInfo> ret = new ArrayList<UserInfo>();
- UserInfo parent = null;
- for (UserInfo ui : mUserInfos) {
- if (ui.id == userId) {
- parent = ui;
- break;
- }
- }
- if (parent == null) {
- return ret;
- }
- ret.add(parent);
- for (UserInfo ui : mUserInfos) {
- if (ui.id == userId) {
- continue;
- }
- if (ui.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
- && ui.profileGroupId == parent.profileGroupId) {
- ret.add(ui);
- }
- }
- return ret;
+ return getProfiles(userId);
}
}
);
+ when(userManager.getProfileIdsWithDisabled(anyInt())).thenAnswer(
+ new Answer<int[]>() {
+ @Override
+ public int[] answer(InvocationOnMock invocation) throws Throwable {
+ final int userId = (int) invocation.getArguments()[0];
+ List<UserInfo> profiles = getProfiles(userId);
+ int[] results = new int[profiles.size()];
+ for (int i = 0; i < results.length; i++) {
+ results[i] = profiles.get(i).id;
+ }
+ return results;
+ }
+ }
+ );
+
// Create a data directory.
final File dir = new File(dataDir, "user" + userId);
@@ -377,6 +370,31 @@
return dir;
}
+ private List<UserInfo> getProfiles(int userId) {
+ final ArrayList<UserInfo> ret = new ArrayList<UserInfo>();
+ UserInfo parent = null;
+ for (UserInfo ui : mUserInfos) {
+ if (ui.id == userId) {
+ parent = ui;
+ break;
+ }
+ }
+ if (parent == null) {
+ return ret;
+ }
+ ret.add(parent);
+ for (UserInfo ui : mUserInfos) {
+ if (ui.id == userId) {
+ continue;
+ }
+ if (ui.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
+ && ui.profileGroupId == parent.profileGroupId) {
+ ret.add(ui);
+ }
+ }
+ return ret;
+ }
+
/**
* Add multiple users at once. They'll all have flag 0.
*/
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
index baa5d36..ce02a79 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.*;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -172,6 +173,11 @@
public String getPackageName() {
return mInjectedClientPackage;
}
+
+ @Override
+ public int getUserId() {
+ return getCallingUserId();
+ }
}
/** Context used in the service side */
@@ -190,6 +196,11 @@
public void startActivityAsUser(@RequiresPermission Intent intent, @Nullable Bundle options,
UserHandle userId) {
}
+
+ @Override
+ public int getUserId() {
+ return UserHandle.USER_SYSTEM;
+ }
}
/** ShortcutService with injection override methods. */
@@ -205,7 +216,8 @@
String injectShortcutManagerConstants() {
return ConfigConstants.KEY_RESET_INTERVAL_SEC + "=" + (INTERVAL / 1000) + ","
+ ConfigConstants.KEY_MAX_SHORTCUTS + "=" + MAX_SHORTCUTS + ","
- + ConfigConstants.KEY_MAX_DAILY_UPDATES + "=" + MAX_DAILY_UPDATES + ","
+ + ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "="
+ + MAX_UPDATES_PER_INTERVAL + ","
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=" + MAX_ICON_DIMENSION + ","
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "="
+ MAX_ICON_DIMENSION_LOWRAM + ","
@@ -454,7 +466,7 @@
private static final int MAX_SHORTCUTS = 10;
- private static final int MAX_DAILY_UPDATES = 3;
+ private static final int MAX_UPDATES_PER_INTERVAL = 3;
private static final int MAX_ICON_DIMENSION = 128;
@@ -598,14 +610,6 @@
addPackage(packageName, uid, version, packageName);
}
- private <T> List<T> list(T... array) {
- return Arrays.asList(array);
- }
-
- private <T> Set<T> set(Set<T> in) {
- return new ArraySet<T>(in);
- }
-
private Signature[] genSignatures(String... signatures) {
final Signature[] sigs = new Signature[signatures.length];
for (int i = 0; i < signatures.length; i++){
@@ -789,33 +793,6 @@
runTestOnUiThread(() -> {});
}
- public static Bundle makeBundle(Object... keysAndValues) {
- Preconditions.checkState((keysAndValues.length % 2) == 0);
-
- if (keysAndValues.length == 0) {
- return null;
- }
- final Bundle ret = new Bundle();
-
- for (int i = keysAndValues.length - 2; i >= 0; i -= 2) {
- final String key = keysAndValues[i].toString();
- final Object value = keysAndValues[i + 1];
-
- if (value == null) {
- ret.putString(key, null);
- } else if (value instanceof Integer) {
- ret.putInt(key, (Integer) value);
- } else if (value instanceof String) {
- ret.putString(key, (String) value);
- } else if (value instanceof Bundle) {
- ret.putBundle(key, (Bundle) value);
- } else {
- fail("Type not supported yet: " + value.getClass().getName());
- }
- }
- return ret;
- }
-
/**
* Make a shortcut with an ID.
*/
@@ -913,20 +890,6 @@
return new ComponentName(mClientContext, clazz);
}
- private <T> Set<T> makeSet(T... values) {
- final HashSet<T> ret = new HashSet<>();
- for (T s : values) {
- ret.add(s);
- }
- return ret;
- }
-
- private static void resetAll(Collection<?> mocks) {
- for (Object o : mocks) {
- reset(o);
- }
- }
-
@NonNull
private ShortcutInfo findById(List<ShortcutInfo> list, String id) {
for (ShortcutInfo s : list) {
@@ -947,59 +910,8 @@
assertEquals(expectedNextResetTime, mService.getNextResetTimeLocked());
}
- @NonNull
- private List<ShortcutInfo> assertShortcutIds(@NonNull List<ShortcutInfo> actualShortcuts,
- String... expectedIds) {
- final HashSet<String> expected = new HashSet<>(list(expectedIds));
- final HashSet<String> actual = new HashSet<>();
- for (ShortcutInfo s : actualShortcuts) {
- actual.add(s.getId());
- }
-
- // Compare the sets.
- assertEquals(expected, actual);
- return actualShortcuts;
- }
-
- @NonNull
- private List<ShortcutInfo> assertAllHaveIntents(
- @NonNull List<ShortcutInfo> actualShortcuts) {
- for (ShortcutInfo s : actualShortcuts) {
- assertNotNull("ID " + s.getId(), s.getIntent());
- }
- return actualShortcuts;
- }
-
- @NonNull
- private List<ShortcutInfo> assertAllNotHaveIntents(
- @NonNull List<ShortcutInfo> actualShortcuts) {
- for (ShortcutInfo s : actualShortcuts) {
- assertNull("ID " + s.getId(), s.getIntent());
- }
- return actualShortcuts;
- }
-
- @NonNull
- private List<ShortcutInfo> assertAllHaveTitle(
- @NonNull List<ShortcutInfo> actualShortcuts) {
- for (ShortcutInfo s : actualShortcuts) {
- assertNotNull("ID " + s.getId(), s.getTitle());
- }
- return actualShortcuts;
- }
-
- @NonNull
- private List<ShortcutInfo> assertAllNotHaveTitle(
- @NonNull List<ShortcutInfo> actualShortcuts) {
- for (ShortcutInfo s : actualShortcuts) {
- assertNull("ID " + s.getId(), s.getTitle());
- }
- return actualShortcuts;
- }
-
- @NonNull
- private List<ShortcutInfo> assertAllNotHaveIcon(
- @NonNull List<ShortcutInfo> actualShortcuts) {
+ public static List<ShortcutInfo> assertAllNotHaveIcon(
+ List<ShortcutInfo> actualShortcuts) {
for (ShortcutInfo s : actualShortcuts) {
assertNull("ID " + s.getId(), s.getIcon());
}
@@ -1007,35 +919,6 @@
}
@NonNull
- private List<ShortcutInfo> assertAllHaveIconResId(
- @NonNull List<ShortcutInfo> actualShortcuts) {
- for (ShortcutInfo s : actualShortcuts) {
- assertTrue("ID " + s.getId() + " not have icon res ID", s.hasIconResource());
- assertFalse("ID " + s.getId() + " shouldn't have icon FD", s.hasIconFile());
- }
- return actualShortcuts;
- }
-
- @NonNull
- private List<ShortcutInfo> assertAllHaveIconFile(
- @NonNull List<ShortcutInfo> actualShortcuts) {
- for (ShortcutInfo s : actualShortcuts) {
- assertFalse("ID " + s.getId() + " shouldn't have icon res ID", s.hasIconResource());
- assertTrue("ID " + s.getId() + " not have icon FD", s.hasIconFile());
- }
- return actualShortcuts;
- }
-
- @NonNull
- private List<ShortcutInfo> assertAllHaveIcon(
- @NonNull List<ShortcutInfo> actualShortcuts) {
- for (ShortcutInfo s : actualShortcuts) {
- assertTrue("ID " + s.getId() + " has no icon ", s.hasIconFile() || s.hasIconResource());
- }
- return actualShortcuts;
- }
-
- @NonNull
private List<ShortcutInfo> assertAllHaveFlags(@NonNull List<ShortcutInfo> actualShortcuts,
int shortcutFlags) {
for (ShortcutInfo s : actualShortcuts) {
@@ -1045,87 +928,6 @@
return actualShortcuts;
}
- @NonNull
- private List<ShortcutInfo> assertAllKeyFieldsOnly(
- @NonNull List<ShortcutInfo> actualShortcuts) {
- for (ShortcutInfo s : actualShortcuts) {
- assertTrue("ID " + s.getId(), s.hasKeyFieldsOnly());
- }
- return actualShortcuts;
- }
-
- @NonNull
- private List<ShortcutInfo> assertAllNotKeyFieldsOnly(
- @NonNull List<ShortcutInfo> actualShortcuts) {
- for (ShortcutInfo s : actualShortcuts) {
- assertFalse("ID " + s.getId(), s.hasKeyFieldsOnly());
- }
- return actualShortcuts;
- }
-
- @NonNull
- private List<ShortcutInfo> assertAllDynamic(@NonNull List<ShortcutInfo> actualShortcuts) {
- return assertAllHaveFlags(actualShortcuts, ShortcutInfo.FLAG_DYNAMIC);
- }
-
- @NonNull
- private List<ShortcutInfo> assertAllPinned(@NonNull List<ShortcutInfo> actualShortcuts) {
- return assertAllHaveFlags(actualShortcuts, ShortcutInfo.FLAG_PINNED);
- }
-
- @NonNull
- private List<ShortcutInfo> assertAllDynamicOrPinned(
- @NonNull List<ShortcutInfo> actualShortcuts) {
- for (ShortcutInfo s : actualShortcuts) {
- assertTrue("ID " + s.getId(), s.isDynamic() || s.isPinned());
- }
- return actualShortcuts;
- }
-
- private void assertDynamicOnly(ShortcutInfo si) {
- assertTrue(si.isDynamic());
- assertFalse(si.isPinned());
- }
-
- private void assertPinnedOnly(ShortcutInfo si) {
- assertFalse(si.isDynamic());
- assertTrue(si.isPinned());
- }
-
- private void assertDynamicAndPinned(ShortcutInfo si) {
- assertTrue(si.isDynamic());
- assertTrue(si.isPinned());
- }
-
- private void assertBitmapSize(int expectedWidth, int expectedHeight, @NonNull Bitmap bitmap) {
- assertEquals("width", expectedWidth, bitmap.getWidth());
- assertEquals("height", expectedHeight, bitmap.getHeight());
- }
-
- private <T> void assertAllUnique(Collection<T> list) {
- final Set<Object> set = new HashSet<>();
- for (T item : list) {
- if (set.contains(item)) {
- fail("Duplicate item found: " + item + " (in the list: " + list + ")");
- }
- set.add(item);
- }
- }
-
- @NonNull
- private Bitmap pfdToBitmap(@NonNull ParcelFileDescriptor pfd) {
- Preconditions.checkNotNull(pfd);
- try {
- return BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
- } finally {
- IoUtils.closeQuietly(pfd);
- }
- }
-
- private void assertBundleEmpty(BaseBundle b) {
- assertTrue(b == null || b.size() == 0);
- }
-
private ShortcutInfo getPackageShortcut(String packageName, String shortcutId, int userId) {
return mService.getPackageShortcutForTest(packageName, shortcutId, userId);
}
@@ -1152,9 +954,25 @@
return intentCaptor.getValue();
}
+ private Intent launchShortcutAndGetIntent_withShortcutInfo(
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ reset(mServiceContext);
+
+ assertTrue(mLauncherApps.startShortcut(
+ getShortcutInfoAsLauncher(packageName, shortcutId, userId), null, null));
+
+ final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mServiceContext).startActivityAsUser(
+ intentCaptor.capture(),
+ any(Bundle.class),
+ eq(UserHandle.of(userId)));
+ return intentCaptor.getValue();
+ }
+
private void assertShortcutLaunchable(@NonNull String packageName, @NonNull String shortcutId,
int userId) {
assertNotNull(launchShortcutAndGetIntent(packageName, shortcutId, userId));
+ assertNotNull(launchShortcutAndGetIntent_withShortcutInfo(packageName, shortcutId, userId));
}
private void assertShortcutNotLaunchable(@NonNull String packageName,
@@ -1193,6 +1011,14 @@
return getLauncherShortcuts(launcher, userId, ShortcutQuery.FLAG_GET_PINNED);
}
+ private ShortcutInfo getShortcutInfoAsLauncher(String packageName, String shortcutId,
+ int userId) {
+ final List<ShortcutInfo> infoList =
+ mLauncherApps.getShortcutInfo(packageName, list(shortcutId),
+ UserHandle.of(userId));
+ assertEquals("No shortcutInfo found (or too many of them)", 1, infoList.size());
+ return infoList.get(0);
+ }
private Intent genPackageDeleteIntent(String pakcageName, int userId) {
Intent i = new Intent(Intent.ACTION_PACKAGE_REMOVED);
@@ -1249,7 +1075,7 @@
assertResetTimes(START_TIME + INTERVAL, START_TIME + 2 * INTERVAL);
- // Advance further; 4 days since start.
+ // Advance further; 4 hours since start.
mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL + 50;
assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
@@ -1286,14 +1112,14 @@
mService.updateConfigurationLocked(
ConfigConstants.KEY_RESET_INTERVAL_SEC + "=123,"
+ ConfigConstants.KEY_MAX_SHORTCUTS + "=4,"
- + ConfigConstants.KEY_MAX_DAILY_UPDATES + "=5,"
+ + ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=5,"
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=100,"
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "=50,"
+ ConfigConstants.KEY_ICON_FORMAT + "=WEBP,"
+ ConfigConstants.KEY_ICON_QUALITY + "=75");
assertEquals(123000, mService.getResetIntervalForTest());
assertEquals(4, mService.getMaxDynamicShortcutsForTest());
- assertEquals(5, mService.getMaxDailyUpdatesForTest());
+ assertEquals(5, mService.getMaxUpdatesPerIntervalForTest());
assertEquals(100, mService.getMaxIconDimensionForTest());
assertEquals(CompressFormat.WEBP, mService.getIconPersistFormatForTest());
assertEquals(75, mService.getIconPersistQualityForTest());
@@ -1309,8 +1135,8 @@
assertEquals(ShortcutService.DEFAULT_MAX_SHORTCUTS_PER_APP,
mService.getMaxDynamicShortcutsForTest());
- assertEquals(ShortcutService.DEFAULT_MAX_DAILY_UPDATES,
- mService.getMaxDailyUpdatesForTest());
+ assertEquals(ShortcutService.DEFAULT_MAX_UPDATES_PER_INTERVAL,
+ mService.getMaxUpdatesPerIntervalForTest());
assertEquals(50, mService.getMaxIconDimensionForTest());
@@ -1329,7 +1155,7 @@
/** Test for {@link android.content.pm.ShortcutManager#getRemainingCallCount()} */
public void testGetRemainingCallCount() {
- assertEquals(MAX_DAILY_UPDATES, mManager.getRemainingCallCount());
+ assertEquals(MAX_UPDATES_PER_INTERVAL, mManager.getRemainingCallCount());
}
/** Test for {@link android.content.pm.ShortcutManager#getRateLimitResetTime()} */
@@ -1416,61 +1242,71 @@
mManager.getDynamicShortcuts()),
"shortcut1");
- assertTrue(mManager.addDynamicShortcut(si2));
+ assertTrue(mManager.addDynamicShortcuts(list(si2, si3)));
assertEquals(1, mManager.getRemainingCallCount());
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
- "shortcut1", "shortcut2");
+ "shortcut1", "shortcut2", "shortcut3");
- // Add with the same ID
- assertTrue(mManager.addDynamicShortcut(makeShortcut("shortcut1")));
+ // This should not crash. It'll still consume the quota.
+ assertTrue(mManager.addDynamicShortcuts(list()));
assertEquals(0, mManager.getRemainingCallCount());
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
- "shortcut1", "shortcut2");
+ "shortcut1", "shortcut2", "shortcut3");
+
+ mInjectedCurrentTimeLillis += INTERVAL; // reset
+
+ // Add with the same ID
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("shortcut1"))));
+ assertEquals(2, mManager.getRemainingCallCount());
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut1", "shortcut2", "shortcut3");
// TODO Check max number
// TODO Check fields.
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
- assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
});
}
- public void testDeleteDynamicShortcut() {
+ public void testDeleteDynamicShortcuts() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
final ShortcutInfo si2 = makeShortcut("shortcut2");
final ShortcutInfo si3 = makeShortcut("shortcut3");
+ final ShortcutInfo si4 = makeShortcut("shortcut4");
- assertTrue(mManager.setDynamicShortcuts(list(si1, si2, si3)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2, si3, si4)));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
- "shortcut1", "shortcut2", "shortcut3");
+ "shortcut1", "shortcut2", "shortcut3", "shortcut4");
assertEquals(2, mManager.getRemainingCallCount());
- mManager.deleteDynamicShortcut("shortcut1");
+ mManager.removeDynamicShortcuts(list("shortcut1"));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
- "shortcut2", "shortcut3");
+ "shortcut2", "shortcut3", "shortcut4");
- mManager.deleteDynamicShortcut("shortcut1");
+ mManager.removeDynamicShortcuts(list("shortcut1"));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
- "shortcut2", "shortcut3");
+ "shortcut2", "shortcut3", "shortcut4");
- mManager.deleteDynamicShortcut("shortcutXXX");
+ mManager.removeDynamicShortcuts(list("shortcutXXX"));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
- "shortcut2", "shortcut3");
+ "shortcut2", "shortcut3", "shortcut4");
- mManager.deleteDynamicShortcut("shortcut2");
+ mManager.removeDynamicShortcuts(list("shortcut2", "shortcut4"));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut3");
- mManager.deleteDynamicShortcut("shortcut3");
+ mManager.removeDynamicShortcuts(list("shortcut3"));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()));
@@ -1490,7 +1326,7 @@
assertEquals(2, mManager.getRemainingCallCount());
- mManager.deleteAllDynamicShortcuts();
+ mManager.removeAllDynamicShortcuts();
assertEquals(0, mManager.getDynamicShortcuts().size());
assertEquals(2, mManager.getRemainingCallCount());
@@ -1558,7 +1394,7 @@
assertEquals(0, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
- // 4 days later...
+ // 4 hours later...
mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL;
assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(2, mManager.getRemainingCallCount());
@@ -1684,7 +1520,7 @@
assertFalse(mManager.setDynamicShortcuts(list(si2)));
}
- public void testIcons() {
+ public void testIcons() throws IOException {
final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
final Icon res64x64 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64);
final Icon res512x512 = Icon.createWithResource(getTestContext(), R.drawable.black_512x512);
@@ -1728,6 +1564,16 @@
"res64x64",
"none");
+ // Different profile. Note the names and the contents don't match.
+ setCaller(CALLING_PACKAGE_1, USER_P0);
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcutWithIcon("res32x32", res512x512),
+ makeShortcutWithIcon("bmp32x32", bmp512x512)
+ )));
+ assertShortcutIds(assertAllNotHaveIcon(mManager.getDynamicShortcuts()),
+ "res32x32",
+ "bmp32x32");
+
// Re-initialize and load from the files.
mService.saveDirtyInfo();
initService();
@@ -1737,61 +1583,90 @@
setCaller(LAUNCHER_1);
// Check hasIconResource()/hasIconFile().
- assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, list("res32x32"),
- getCallingUser())), "res32x32");
+ assertShortcutIds(assertAllHaveIconResId(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_0))),
+ "res32x32");
- assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, list("res64x64"), getCallingUser())),
+ assertShortcutIds(assertAllHaveIconResId(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res64x64", USER_0))),
"res64x64");
- assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, list("bmp32x32"), getCallingUser())),
+ assertShortcutIds(assertAllHaveIconFile(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_0))),
"bmp32x32");
- assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, list("bmp64x64"), getCallingUser())),
+ assertShortcutIds(assertAllHaveIconFile(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp64x64", USER_0))),
"bmp64x64");
- assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, list("bmp512x512"), getCallingUser())),
+ assertShortcutIds(assertAllHaveIconFile(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0))),
"bmp512x512");
+ assertShortcutIds(assertAllHaveIconResId(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_P0))),
+ "res32x32");
+ assertShortcutIds(assertAllHaveIconFile(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_P0))),
+ "bmp32x32");
+
// Check
assertEquals(
R.drawable.black_32x32,
mLauncherApps.getShortcutIconResId(
- makePackageShortcut(CALLING_PACKAGE_1, "res32x32"), getCallingUser()));
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_0)));
assertEquals(
R.drawable.black_64x64,
mLauncherApps.getShortcutIconResId(
-
- makePackageShortcut(CALLING_PACKAGE_1, "res64x64"), getCallingUser()));
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res64x64", USER_0)));
assertEquals(
0, // because it's not a resource
mLauncherApps.getShortcutIconResId(
- makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUser()));
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_0)));
assertEquals(
0, // because it's not a resource
mLauncherApps.getShortcutIconResId(
- makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUser()));
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp64x64", USER_0)));
assertEquals(
0, // because it's not a resource
mLauncherApps.getShortcutIconResId(
- makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUser()));
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0)));
bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
- makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUser()));
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_0)));
assertBitmapSize(32, 32, bmp);
bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
- makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUser()));
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp64x64", USER_0)));
assertBitmapSize(64, 64, bmp);
bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
- makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUser()));
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0)));
+ assertBitmapSize(128, 128, bmp);
+
+ assertEquals(
+ R.drawable.black_512x512,
+ mLauncherApps.getShortcutIconResId(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_P0)));
+ // Should be 512x512, so shrunk.
+ bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_P0)));
+ assertBitmapSize(128, 128, bmp);
+
+ // Also check the overload APIs too.
+ assertEquals(
+ R.drawable.black_32x32,
+ mLauncherApps.getShortcutIconResId(CALLING_PACKAGE_1, "res32x32", HANDLE_USER_0));
+ assertEquals(
+ R.drawable.black_64x64,
+ mLauncherApps.getShortcutIconResId(CALLING_PACKAGE_1, "res64x64", HANDLE_USER_0));
+ assertEquals(
+ R.drawable.black_512x512,
+ mLauncherApps.getShortcutIconResId(CALLING_PACKAGE_1, "res32x32", HANDLE_USER_P0));
+ bmp = pfdToBitmap(
+ mLauncherApps.getShortcutIconFd(CALLING_PACKAGE_1, "bmp32x32", HANDLE_USER_P0));
assertBitmapSize(128, 128, bmp);
// TODO Test the content URI case too.
@@ -1927,13 +1802,13 @@
getCallingUser());
});
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
- mManager.deleteDynamicShortcut("s1");
- mManager.deleteDynamicShortcut("s2");
+ mManager.removeDynamicShortcuts(list("s1"));
+ mManager.removeDynamicShortcuts(list("s2"));
});
runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
- mManager.deleteDynamicShortcut("s1");
- mManager.deleteDynamicShortcut("s3");
- mManager.deleteDynamicShortcut("s5");
+ mManager.removeDynamicShortcuts(list("s1"));
+ mManager.removeDynamicShortcuts(list("s3"));
+ mManager.removeDynamicShortcuts(list("s5"));
});
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
assertShortcutIds(assertAllDynamic(
@@ -2034,9 +1909,16 @@
private static ShortcutQuery buildQuery(long changedSince,
String packageName, ComponentName componentName,
/* @ShortcutQuery.QueryFlags */ int flags) {
+ return buildQuery(changedSince, packageName, null, componentName, flags);
+ }
+
+ private static ShortcutQuery buildQuery(long changedSince,
+ String packageName, List<String> shortcutIds, ComponentName componentName,
+ /* @ShortcutQuery.QueryFlags */ int flags) {
final ShortcutQuery q = new ShortcutQuery();
q.setChangedSince(changedSince);
q.setPackage(packageName);
+ q.setShortcutIds(shortcutIds);
q.setActivity(componentName);
q.setQueryFlags(flags);
return q;
@@ -2110,6 +1992,36 @@
getCallingUser())),
"s2", "s3"))));
+ // With ID.
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_2, list("s3"),
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser())),
+ "s3"))));
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_2, list("s3", "s2", "ss"),
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser())),
+ "s2", "s3"))));
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_2, list("s3x", "s2x"),
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser()))
+ /* empty */))));
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_2, list(),
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser()))
+ /* empty */))));
+
// Pin some shortcuts.
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
list("s3", "s4"), getCallingUser());
@@ -2132,6 +2044,13 @@
getCallingUser())),
"s1", "s3");
+ assertExpectException(
+ IllegalArgumentException.class, "package name must also be set", () -> {
+ mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 0, /* package= */ null, list("id"),
+ /* activity =*/ null, /* flags */ 0), getCallingUser());
+ });
+
// TODO More tests: pinned but dynamic, filter by activity
}
@@ -2181,7 +2100,7 @@
// Delete some.
setCaller(CALLING_PACKAGE_1);
assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
- mManager.deleteDynamicShortcut("s2");
+ mManager.removeDynamicShortcuts(list("s2"));
assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
dumpsysOnLogcat();
@@ -2246,19 +2165,19 @@
// Delete some.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
- mManager.deleteDynamicShortcut("s2");
+ mManager.removeDynamicShortcuts(list("s2"));
assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
});
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
- mManager.deleteDynamicShortcut("s3");
+ mManager.removeDynamicShortcuts(list("s3"));
assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
});
runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
- mManager.deleteDynamicShortcut("s2");
+ mManager.removeDynamicShortcuts(list("s2"));
assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
});
@@ -2310,7 +2229,7 @@
// Delete some.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertShortcutIds(mManager.getPinnedShortcuts(), "s3");
- mManager.deleteDynamicShortcut("s3");
+ mManager.removeDynamicShortcuts(list("s3"));
assertShortcutIds(mManager.getPinnedShortcuts(), "s3");
});
@@ -2318,8 +2237,8 @@
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
assertShortcutIds(mManager.getPinnedShortcuts(), "s1", "s2");
- mManager.deleteDynamicShortcut("s1");
- mManager.deleteDynamicShortcut("s3");
+ mManager.removeDynamicShortcuts(list("s1"));
+ mManager.removeDynamicShortcuts(list("s3"));
assertShortcutIds(mManager.getPinnedShortcuts(), "s1", "s2");
});
@@ -2428,13 +2347,13 @@
// Delete all dynamic.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
- mManager.deleteAllDynamicShortcuts();
+ mManager.removeAllDynamicShortcuts();
assertEquals(0, mManager.getDynamicShortcuts().size());
assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s1", "s2", "s3");
});
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
- mManager.deleteAllDynamicShortcuts();
+ mManager.removeAllDynamicShortcuts();
assertEquals(0, mManager.getDynamicShortcuts().size());
assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s2", "s1");
@@ -2469,7 +2388,7 @@
});
// Re-publish s1.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
- assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()), "s1");
assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s1", "s2", "s3");
@@ -3071,7 +2990,7 @@
// Just to make it complicated, delete some.
setCaller(CALLING_PACKAGE_1);
- mManager.deleteDynamicShortcut("s2");
+ mManager.removeDynamicShortcuts(list("s2"));
// intent and check.
setCaller(LAUNCHER_1);
@@ -3143,11 +3062,11 @@
any(UserHandle.class)
);
- // Test for addDynamicShortcut.
+ // Test for addDynamicShortcuts.
reset(c0);
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
- dumpsysOnLogcat("before addDynamicShortcut");
- assertTrue(mManager.addDynamicShortcut(makeShortcut("s4")));
+ dumpsysOnLogcat("before addDynamicShortcuts");
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s4"))));
});
waitOnMainThread();
@@ -3163,7 +3082,7 @@
// Test for remove
reset(c0);
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
- mManager.deleteDynamicShortcut("s1");
+ mManager.removeDynamicShortcuts(list("s1"));
});
waitOnMainThread();
@@ -3196,7 +3115,7 @@
// Test for deleteAll
reset(c0);
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
- mManager.deleteAllDynamicShortcuts();
+ mManager.removeAllDynamicShortcuts();
});
waitOnMainThread();
@@ -3224,20 +3143,6 @@
assertEquals(0, shortcuts.getValue().size());
}
- private void assertCallbackNotReceived(LauncherApps.Callback mock) {
- verify(mock, times(0)).onShortcutsChanged(anyString(), anyList(),
- any(UserHandle.class));
- }
-
- private void assertCallbackReceived(LauncherApps.Callback mock,
- UserHandle user, String packageName, String... ids) {
- ArgumentCaptor<List> shortcutsCaptor = ArgumentCaptor.forClass(List.class);
-
- verify(mock, times(1)).onShortcutsChanged(eq(packageName), shortcutsCaptor.capture(),
- eq(user));
- assertShortcutIds(shortcutsCaptor.getValue(), ids);
- }
-
public void testLauncherCallback_crossProfile() throws Throwable {
prepareCrossProfileDataSet();
@@ -3284,7 +3189,7 @@
resetAll(all);
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
- mManager.deleteDynamicShortcut("x");
+ mManager.removeDynamicShortcuts(list());
});
waitOnMainThread();
@@ -3301,7 +3206,7 @@
resetAll(all);
runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
- mManager.deleteDynamicShortcut("x");
+ mManager.removeDynamicShortcuts(list());
});
waitOnMainThread();
@@ -3319,7 +3224,7 @@
resetAll(all);
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
- mManager.deleteDynamicShortcut("x");
+ mManager.removeDynamicShortcuts(list());
});
waitOnMainThread();
@@ -3339,7 +3244,7 @@
resetAll(all);
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
- mManager.deleteDynamicShortcut("x");
+ mManager.removeDynamicShortcuts(list());
});
waitOnMainThread();
@@ -3359,7 +3264,7 @@
resetAll(all);
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
- mManager.deleteDynamicShortcut("x");
+ mManager.removeDynamicShortcuts(list());
});
waitOnMainThread();
@@ -3583,16 +3488,16 @@
// Remove all dynamic shortcuts; now all shortcuts are just pinned.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
- mManager.deleteAllDynamicShortcuts();
+ mManager.removeAllDynamicShortcuts();
});
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
- mManager.deleteAllDynamicShortcuts();
+ mManager.removeAllDynamicShortcuts();
});
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
- mManager.deleteAllDynamicShortcuts();
+ mManager.removeAllDynamicShortcuts();
});
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
- mManager.deleteAllDynamicShortcuts();
+ mManager.removeAllDynamicShortcuts();
});
@@ -3607,18 +3512,18 @@
// Check the registered packages.
dumpsysOnLogcat();
- assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- set(user0.getAllPackages().keySet()));
- assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- set(user10.getAllPackages().keySet()));
+ assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+ hashSet(user0.getAllPackages().keySet()));
+ assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+ hashSet(user10.getAllPackages().keySet()));
assertEquals(
- makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ set(PackageWithUser.of(USER_0, LAUNCHER_1),
PackageWithUser.of(USER_0, LAUNCHER_2)),
- set(user0.getAllLaunchers().keySet()));
+ hashSet(user0.getAllLaunchers().keySet()));
assertEquals(
- makeSet(PackageWithUser.of(USER_10, LAUNCHER_1),
+ set(PackageWithUser.of(USER_10, LAUNCHER_1),
PackageWithUser.of(USER_10, LAUNCHER_2)),
- set(user10.getAllLaunchers().keySet()));
+ hashSet(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_1", "s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3639,18 +3544,18 @@
mService.cleanUpPackageLocked("abc", USER_0, USER_0);
// No changes.
- assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- set(user0.getAllPackages().keySet()));
- assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- set(user10.getAllPackages().keySet()));
+ assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+ hashSet(user0.getAllPackages().keySet()));
+ assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+ hashSet(user10.getAllPackages().keySet()));
assertEquals(
- makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ set(PackageWithUser.of(USER_0, LAUNCHER_1),
PackageWithUser.of(USER_0, LAUNCHER_2)),
- set(user0.getAllLaunchers().keySet()));
+ hashSet(user0.getAllLaunchers().keySet()));
assertEquals(
- makeSet(PackageWithUser.of(USER_10, LAUNCHER_1),
+ set(PackageWithUser.of(USER_10, LAUNCHER_1),
PackageWithUser.of(USER_10, LAUNCHER_2)),
- set(user10.getAllLaunchers().keySet()));
+ hashSet(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_1", "s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3670,18 +3575,18 @@
uninstallPackage(USER_0, CALLING_PACKAGE_1);
mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_0, USER_0);
- assertEquals(makeSet(CALLING_PACKAGE_2),
- set(user0.getAllPackages().keySet()));
- assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- set(user10.getAllPackages().keySet()));
+ assertEquals(set(CALLING_PACKAGE_2),
+ hashSet(user0.getAllPackages().keySet()));
+ assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+ hashSet(user10.getAllPackages().keySet()));
assertEquals(
- makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ set(PackageWithUser.of(USER_0, LAUNCHER_1),
PackageWithUser.of(USER_0, LAUNCHER_2)),
- set(user0.getAllLaunchers().keySet()));
+ hashSet(user0.getAllLaunchers().keySet()));
assertEquals(
- makeSet(PackageWithUser.of(USER_10, LAUNCHER_1),
+ set(PackageWithUser.of(USER_10, LAUNCHER_1),
PackageWithUser.of(USER_10, LAUNCHER_2)),
- set(user10.getAllLaunchers().keySet()));
+ hashSet(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3701,17 +3606,17 @@
uninstallPackage(USER_10, LAUNCHER_1);
mService.cleanUpPackageLocked(LAUNCHER_1, USER_10, USER_10);
- assertEquals(makeSet(CALLING_PACKAGE_2),
- set(user0.getAllPackages().keySet()));
- assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- set(user10.getAllPackages().keySet()));
+ assertEquals(set(CALLING_PACKAGE_2),
+ hashSet(user0.getAllPackages().keySet()));
+ assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+ hashSet(user10.getAllPackages().keySet()));
assertEquals(
- makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ set(PackageWithUser.of(USER_0, LAUNCHER_1),
PackageWithUser.of(USER_0, LAUNCHER_2)),
- set(user0.getAllLaunchers().keySet()));
+ hashSet(user0.getAllLaunchers().keySet()));
assertEquals(
- makeSet(PackageWithUser.of(USER_10, LAUNCHER_2)),
- set(user10.getAllLaunchers().keySet()));
+ set(PackageWithUser.of(USER_10, LAUNCHER_2)),
+ hashSet(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3729,17 +3634,17 @@
uninstallPackage(USER_10, CALLING_PACKAGE_2);
mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_10, USER_10);
- assertEquals(makeSet(CALLING_PACKAGE_2),
- set(user0.getAllPackages().keySet()));
- assertEquals(makeSet(CALLING_PACKAGE_1),
- set(user10.getAllPackages().keySet()));
+ assertEquals(set(CALLING_PACKAGE_2),
+ hashSet(user0.getAllPackages().keySet()));
+ assertEquals(set(CALLING_PACKAGE_1),
+ hashSet(user10.getAllPackages().keySet()));
assertEquals(
- makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ set(PackageWithUser.of(USER_0, LAUNCHER_1),
PackageWithUser.of(USER_0, LAUNCHER_2)),
- set(user0.getAllLaunchers().keySet()));
+ hashSet(user0.getAllLaunchers().keySet()));
assertEquals(
- makeSet(PackageWithUser.of(USER_10, LAUNCHER_2)),
- set(user10.getAllLaunchers().keySet()));
+ set(PackageWithUser.of(USER_10, LAUNCHER_2)),
+ hashSet(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3757,17 +3662,17 @@
uninstallPackage(USER_10, LAUNCHER_2);
mService.cleanUpPackageLocked(LAUNCHER_2, USER_10, USER_10);
- assertEquals(makeSet(CALLING_PACKAGE_2),
- set(user0.getAllPackages().keySet()));
- assertEquals(makeSet(CALLING_PACKAGE_1),
- set(user10.getAllPackages().keySet()));
+ assertEquals(set(CALLING_PACKAGE_2),
+ hashSet(user0.getAllPackages().keySet()));
+ assertEquals(set(CALLING_PACKAGE_1),
+ hashSet(user10.getAllPackages().keySet()));
assertEquals(
- makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ set(PackageWithUser.of(USER_0, LAUNCHER_1),
PackageWithUser.of(USER_0, LAUNCHER_2)),
- set(user0.getAllLaunchers().keySet()));
+ hashSet(user0.getAllLaunchers().keySet()));
assertEquals(
- makeSet(),
- set(user10.getAllLaunchers().keySet()));
+ set(),
+ hashSet(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3785,16 +3690,16 @@
uninstallPackage(USER_10, CALLING_PACKAGE_1);
mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_10, USER_10);
- assertEquals(makeSet(CALLING_PACKAGE_2),
- set(user0.getAllPackages().keySet()));
- assertEquals(makeSet(),
- set(user10.getAllPackages().keySet()));
+ assertEquals(set(CALLING_PACKAGE_2),
+ hashSet(user0.getAllPackages().keySet()));
+ assertEquals(set(),
+ hashSet(user10.getAllPackages().keySet()));
assertEquals(
- makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ set(PackageWithUser.of(USER_0, LAUNCHER_1),
PackageWithUser.of(USER_0, LAUNCHER_2)),
- set(user0.getAllLaunchers().keySet()));
- assertEquals(makeSet(),
- set(user10.getAllLaunchers().keySet()));
+ hashSet(user0.getAllLaunchers().keySet()));
+ assertEquals(set(),
+ hashSet(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -4115,22 +4020,22 @@
public void testHandlePackageDelete() {
setCaller(CALLING_PACKAGE_1, USER_0);
- assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
setCaller(CALLING_PACKAGE_2, USER_0);
- assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
setCaller(CALLING_PACKAGE_3, USER_0);
- assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
setCaller(CALLING_PACKAGE_1, USER_10);
- assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
setCaller(CALLING_PACKAGE_2, USER_10);
- assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
setCaller(CALLING_PACKAGE_3, USER_10);
- assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
@@ -4935,7 +4840,7 @@
assertShortcutIds(
mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0)
/* empty */);
- TestUtils.assertExpectException(
+ assertExpectException(
SecurityException.class, "", () -> {
mLauncherApps.getShortcuts(
buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_10);
@@ -5014,7 +4919,7 @@
assertShortcutIds(
mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0),
"s1", "s4");
- TestUtils.assertExpectException(
+ assertExpectException(
SecurityException.class, "unrelated profile", () -> {
mLauncherApps.getShortcuts(
buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_10);
@@ -5030,12 +4935,12 @@
assertShortcutIds(
mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_10)
/* empty */);
- TestUtils.assertExpectException(
+ assertExpectException(
SecurityException.class, "unrelated profile", () -> {
mLauncherApps.getShortcuts(
buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0);
});
- TestUtils.assertExpectException(
+ assertExpectException(
SecurityException.class, "unrelated profile", () -> {
mLauncherApps.getShortcuts(
buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_P0);
@@ -5046,16 +4951,16 @@
// ShortcutInfo tests
public void testShortcutInfoMissingMandatoryFields() {
- TestUtils.assertExpectException(
+ assertExpectException(
IllegalArgumentException.class,
"ID must be provided",
() -> new ShortcutInfo.Builder(getTestContext()).build());
- TestUtils.assertExpectException(
+ assertExpectException(
IllegalArgumentException.class,
"title must be provided",
() -> new ShortcutInfo.Builder(getTestContext()).setId("id").build()
.enforceMandatoryFields());
- TestUtils.assertExpectException(
+ assertExpectException(
NullPointerException.class,
"Intent must be provided",
() -> new ShortcutInfo.Builder(getTestContext()).setId("id").setTitle("x").build()
@@ -5063,12 +4968,15 @@
}
public void testShortcutInfoParcel() {
- ShortcutInfo si = parceled(new ShortcutInfo.Builder(getTestContext())
+ setCaller(CALLING_PACKAGE_1, USER_10);
+ ShortcutInfo si = parceled(new ShortcutInfo.Builder(mClientContext)
.setId("id")
.setTitle("title")
.setIntent(makeIntent("action", ShortcutActivity.class))
.build());
- assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackageName());
+ assertEquals(USER_10, si.getUserId());
+ assertEquals(HANDLE_USER_10, si.getUserHandle());
assertEquals("id", si.getId());
assertEquals("title", si.getTitle());
assertEquals("action", si.getIntent().getAction());
@@ -5083,6 +4991,7 @@
.setTitle("title")
.setText("text")
.setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setCategories(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
.setWeight(123)
.setExtras(pb)
.build();
@@ -5098,6 +5007,7 @@
assertEquals("content://a.b.c/", si.getIcon().getUriString());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
+ assertEquals(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
assertEquals("action", si.getIntent().getAction());
assertEquals("val", si.getIntent().getStringExtra("key"));
assertEquals(123, si.getWeight());
@@ -5109,14 +5019,17 @@
}
public void testShortcutInfoClone() {
+ setCaller(CALLING_PACKAGE_1, USER_11);
+
PersistableBundle pb = new PersistableBundle();
pb.putInt("k", 1);
- ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
+ ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
.setId("id")
.setActivityComponent(new ComponentName("a", "b"))
.setIcon(Icon.createWithContentUri("content://a.b.c/"))
.setTitle("title")
.setText("text")
+ .setCategories(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
.setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
.setWeight(123)
.setExtras(pb)
@@ -5127,12 +5040,15 @@
ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
- assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals(USER_11, si.getUserId());
+ assertEquals(HANDLE_USER_11, si.getUserHandle());
+ assertEquals(mClientContext.getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
assertEquals("content://a.b.c/", si.getIcon().getUriString());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
+ assertEquals(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
assertEquals("action", si.getIntent().getAction());
assertEquals("val", si.getIntent().getStringExtra("key"));
assertEquals(123, si.getWeight());
@@ -5144,12 +5060,13 @@
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
- assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
assertEquals(null, si.getIcon());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
+ assertEquals(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
assertEquals("action", si.getIntent().getAction());
assertEquals("val", si.getIntent().getStringExtra("key"));
assertEquals(123, si.getWeight());
@@ -5157,39 +5074,85 @@
assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
assertEquals(null, si.getBitmapPath());
- assertEquals(0, si.getIconResourceId());
+
+ assertEquals(456, si.getIconResourceId());
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
- assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
assertEquals(null, si.getIcon());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
+ assertEquals(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
assertEquals(null, si.getIntent());
assertEquals(123, si.getWeight());
assertEquals(1, si.getExtras().getInt("k"));
assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
assertEquals(null, si.getBitmapPath());
- assertEquals(0, si.getIconResourceId());
+
+ assertEquals(456, si.getIconResourceId());
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
- assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(null, si.getActivityComponent());
assertEquals(null, si.getIcon());
assertEquals(null, si.getTitle());
assertEquals(null, si.getText());
+ assertEquals(null, si.getCategories());
assertEquals(null, si.getIntent());
assertEquals(0, si.getWeight());
assertEquals(null, si.getExtras());
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags());
assertEquals(null, si.getBitmapPath());
- assertEquals(0, si.getIconResourceId());
+
+ assertEquals(456, si.getIconResourceId());
+ }
+
+ public void testShortcutInfoClone_minimum() {
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
+ .setId("id")
+ .setTitle("title")
+ .setIntent(makeIntent("action", ShortcutActivity.class))
+ .build();
+ ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
+
+ assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals("id", si.getId());
+ assertEquals("title", si.getTitle());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals(null, si.getCategories());
+
+ si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
+
+ assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals("id", si.getId());
+ assertEquals("title", si.getTitle());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals(null, si.getCategories());
+
+ si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+
+ assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals("id", si.getId());
+ assertEquals("title", si.getTitle());
+ assertEquals(null, si.getIntent());
+ assertEquals(null, si.getCategories());
+
+ si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
+
+ assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals("id", si.getId());
+ assertEquals(null, si.getTitle());
+ assertEquals(null, si.getIntent());
+ assertEquals(null, si.getCategories());
}
public void testShortcutInfoCopyNonNullFieldsFrom() throws InterruptedException {
@@ -5201,6 +5164,7 @@
.setIcon(Icon.createWithContentUri("content://a.b.c/"))
.setTitle("title")
.setText("text")
+ .setCategories(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
.setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
.setWeight(123)
.setExtras(pb)
@@ -5214,38 +5178,57 @@
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
.setActivityComponent(new ComponentName("x", "y")).build());
+ assertEquals("text", si.getText());
assertEquals(new ComponentName("x", "y"), si.getActivityComponent());
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
.setIcon(Icon.createWithContentUri("content://x.y.z/")).build());
+ assertEquals("text", si.getText());
assertEquals("content://x.y.z/", si.getIcon().getUriString());
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
.setTitle("xyz").build());
+ assertEquals("text", si.getText());
assertEquals("xyz", si.getTitle());
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
.setText("xxx").build());
+ assertEquals(123, si.getWeight());
assertEquals("xxx", si.getText());
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setCategories(list()).build());
+ assertEquals("text", si.getText());
+ assertEquals(list(), si.getCategories());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setCategories(list("x")).build());
+ assertEquals("text", si.getText());
+ assertEquals(list("x"), si.getCategories());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
.setIntent(makeIntent("action2", ShortcutActivity.class)).build());
+ assertEquals("text", si.getText());
assertEquals("action2", si.getIntent().getAction());
assertEquals(null, si.getIntent().getStringExtra("key"));
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
.setIntent(makeIntent("action3", ShortcutActivity.class, "key", "x")).build());
+ assertEquals("text", si.getText());
assertEquals("action3", si.getIntent().getAction());
assertEquals("x", si.getIntent().getStringExtra("key"));
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
.setWeight(999).build());
+ assertEquals("text", si.getText());
assertEquals(999, si.getWeight());
@@ -5255,8 +5238,11 @@
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
.setExtras(pb2).build());
+ assertEquals("text", si.getText());
assertEquals(99, si.getExtras().getInt("x"));
+ // Make sure the timestamp gets updated too.
+
final long timestamp = si.getLastChangedTimestamp();
Thread.sleep(2);
@@ -5267,7 +5253,7 @@
}
public void testShortcutInfoSaveAndLoad() throws InterruptedException {
- setCaller(CALLING_PACKAGE_1, USER_0);
+ setCaller(CALLING_PACKAGE_1, USER_10);
final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.black_32x32));
@@ -5280,12 +5266,13 @@
.setIcon(bmp32x32)
.setTitle("title")
.setText("text")
+ .setCategories(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
.setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
.setWeight(123)
.setExtras(pb)
.build();
- mManager.addDynamicShortcut(sorig);
+ mManager.addDynamicShortcuts(list(sorig));
Thread.sleep(2);
final long now = System.currentTimeMillis();
@@ -5293,17 +5280,20 @@
// Save and load.
mService.saveDirtyInfo();
initService();
- mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_10);
ShortcutInfo si;
- si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0);
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_10);
+ assertEquals(USER_10, si.getUserId());
+ assertEquals(HANDLE_USER_10, si.getUserHandle());
assertEquals(CALLING_PACKAGE_1, si.getPackageName());
assertEquals("id", si.getId());
assertEquals(ShortcutActivity2.class.getName(), si.getActivityComponent().getClassName());
assertEquals(null, si.getIcon());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
+ assertEquals(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
assertEquals("action", si.getIntent().getAction());
assertEquals("val", si.getIntent().getStringExtra("key"));
assertEquals(123, si.getWeight());
@@ -5329,12 +5319,13 @@
.setIcon(bmp32x32)
.setTitle("title")
.setText("text")
+ .setCategories(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
.setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
.setWeight(123)
.setExtras(pb)
.build();
- mManager.addDynamicShortcut(sorig);
+ mManager.addDynamicShortcuts(list(sorig));
// Dynamic shortcuts won't be backed up, so we need to pin it.
setCaller(LAUNCHER_1, USER_0);
@@ -5343,6 +5334,8 @@
// Do backup & restore.
backupAndRestore();
+ mService.handleUnlockUser(USER_0); // Load user-0.
+
ShortcutInfo si;
si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0);
@@ -5352,6 +5345,7 @@
assertEquals(null, si.getIcon());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
+ assertEquals(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
assertEquals("action", si.getIntent().getAction());
assertEquals("val", si.getIntent().getStringExtra("key"));
assertEquals(123, si.getWeight());
@@ -5367,7 +5361,7 @@
dumpsysOnLogcat("test1", /* force= */ true);
}
- public void testDumpsys_withIcons() {
+ public void testDumpsys_withIcons() throws IOException {
testIcons();
// Dump after having some icons.
dumpsysOnLogcat("test1", /* force= */ true);
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
new file mode 100644
index 0000000..26b87c5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.webkit;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.webkit.WebViewProviderInfo;
+
+import java.util.HashMap;
+
+public class TestSystemImpl implements SystemInterface {
+ private String mUserProvider = "";
+ private final WebViewProviderInfo[] mPackageConfigs;
+ HashMap<String, PackageInfo> mPackages = new HashMap();
+ private boolean mFallbackLogicEnabled;
+ private final int mNumRelros;
+ private final boolean mIsDebuggable;
+
+ public TestSystemImpl(WebViewProviderInfo[] packageConfigs, boolean fallbackLogicEnabled,
+ int numRelros, boolean isDebuggable) {
+ mPackageConfigs = packageConfigs;
+ mFallbackLogicEnabled = fallbackLogicEnabled;
+ mNumRelros = numRelros;
+ mIsDebuggable = isDebuggable;
+ }
+
+ @Override
+ public WebViewProviderInfo[] getWebViewPackages() {
+ return mPackageConfigs;
+ }
+
+ @Override
+ public int onWebViewProviderChanged(PackageInfo packageInfo) {
+ return mNumRelros;
+ }
+
+ @Override
+ public String getUserChosenWebViewProvider(Context context) { return mUserProvider; }
+
+ @Override
+ public void updateUserSetting(Context context, String newProviderName) {
+ mUserProvider = newProviderName;
+ }
+
+ @Override
+ public void killPackageDependents(String packageName) {}
+
+ @Override
+ public boolean isFallbackLogicEnabled() {
+ return mFallbackLogicEnabled;
+ }
+
+ @Override
+ public void enableFallbackLogic(boolean enable) {
+ mFallbackLogicEnabled = enable;
+ }
+
+ @Override
+ public void uninstallAndDisablePackageForAllUsers(Context context, String packageName) {
+ enablePackageForAllUsers(context, packageName, false);
+ }
+
+ @Override
+ public void enablePackageForAllUsers(Context context, String packageName, boolean enable) {
+ enablePackageForUser(packageName, enable, 0);
+ }
+
+ @Override
+ public void enablePackageForUser(String packageName, boolean enable, int userId) {
+ PackageInfo packageInfo = mPackages.get(packageName);
+ if (packageInfo == null) {
+ throw new IllegalArgumentException("There is no package called " + packageName);
+ }
+ packageInfo.applicationInfo.enabled = enable;
+ setPackageInfo(packageInfo);
+ }
+
+ @Override
+ public boolean systemIsDebuggable() { return mIsDebuggable; }
+
+ @Override
+ public PackageInfo getPackageInfoForProvider(WebViewProviderInfo info) throws
+ NameNotFoundException {
+ PackageInfo ret = mPackages.get(info.packageName);
+ if (ret == null) throw new NameNotFoundException(info.packageName);
+ return ret;
+ }
+
+ public void setPackageInfo(PackageInfo pi) {
+ mPackages.put(pi.packageName, pi);
+ }
+
+ @Override
+ public int getFactoryPackageVersion(String packageName) {
+ return 0;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
new file mode 100644
index 0000000..c00520d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -0,0 +1,613 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.webkit;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.Signature;
+import android.os.Bundle;
+import android.util.Base64;
+import android.test.AndroidTestCase;
+
+import android.webkit.WebViewFactory;
+import android.webkit.WebViewProviderInfo;
+import android.webkit.WebViewProviderResponse;
+
+import java.util.concurrent.CountDownLatch;
+
+import org.hamcrest.Description;
+
+import org.mockito.Mockito;
+import org.mockito.Matchers;
+import org.mockito.ArgumentMatcher;
+
+
+/**
+ * Tests for WebViewUpdateService
+ */
+public class WebViewUpdateServiceTest extends AndroidTestCase {
+ private final static String TAG = WebViewUpdateServiceTest.class.getSimpleName();
+
+ private WebViewUpdateServiceImpl mWebViewUpdateServiceImpl;
+ private TestSystemImpl mTestSystemImpl;
+
+ private static final String WEBVIEW_LIBRARY_FLAG = "com.android.webview.WebViewLibrary";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Creates a new instance.
+ */
+ public WebViewUpdateServiceTest() {
+ }
+
+ private void setupWithPackages(WebViewProviderInfo[] packages) {
+ setupWithPackages(packages, true);
+ }
+
+ private void setupWithPackages(WebViewProviderInfo[] packages,
+ boolean fallbackLogicEnabled) {
+ setupWithPackages(packages, fallbackLogicEnabled, 1);
+ }
+
+ private void setupWithPackages(WebViewProviderInfo[] packages,
+ boolean fallbackLogicEnabled, int numRelros) {
+ setupWithPackages(packages, fallbackLogicEnabled, numRelros,
+ true /* isDebuggable == true -> don't check package signatures */);
+ }
+
+ private void setupWithPackages(WebViewProviderInfo[] packages,
+ boolean fallbackLogicEnabled, int numRelros, boolean isDebuggable) {
+ TestSystemImpl testing = new TestSystemImpl(packages, fallbackLogicEnabled, numRelros,
+ isDebuggable);
+ mTestSystemImpl = Mockito.spy(testing);
+ mWebViewUpdateServiceImpl =
+ new WebViewUpdateServiceImpl(null /*Context*/, mTestSystemImpl);
+ }
+
+ private void setEnabledAndValidPackageInfos(WebViewProviderInfo[] providers) {
+ for(WebViewProviderInfo wpi : providers) {
+ mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName, true /* enabled */,
+ true /* valid */));
+ }
+ }
+
+ private void checkCertainPackageUsedAfterWebViewPreparation(String expectedProviderName,
+ WebViewProviderInfo[] webviewPackages) {
+ checkCertainPackageUsedAfterWebViewPreparation(expectedProviderName, webviewPackages, 1);
+ }
+
+ private void checkCertainPackageUsedAfterWebViewPreparation(String expectedProviderName,
+ WebViewProviderInfo[] webviewPackages, int numRelros) {
+ setupWithPackages(webviewPackages, true, numRelros);
+ // Add (enabled and valid) package infos for each provider
+ setEnabledAndValidPackageInfos(webviewPackages);
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(expectedProviderName)));
+
+ for (int n = 0; n < numRelros; n++) {
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+ }
+
+ WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+ assertEquals(expectedProviderName, response.packageInfo.packageName);
+ }
+
+ // For matching the package name of a PackageInfo
+ private class IsPackageInfoWithName extends ArgumentMatcher<PackageInfo> {
+ private final String mPackageName;
+
+ IsPackageInfoWithName(String name) {
+ mPackageName = name;
+ }
+
+ @Override
+ public boolean matches(Object p) {
+ return ((PackageInfo) p).packageName.equals(mPackageName);
+ }
+
+ // Provide a more useful description in case of mismatch
+ @Override
+ public void describeTo (Description description) {
+ description.appendText(String.format("PackageInfo with name '%s'", mPackageName));
+ }
+ }
+
+ private static PackageInfo createPackageInfo(
+ String packageName, boolean enabled, boolean valid) {
+ PackageInfo p = new PackageInfo();
+ p.packageName = packageName;
+ p.applicationInfo = new ApplicationInfo();
+ p.applicationInfo.enabled = enabled;
+ p.applicationInfo.metaData = new Bundle();
+ if (valid) {
+ // no flag means invalid
+ p.applicationInfo.metaData.putString(WEBVIEW_LIBRARY_FLAG, "blah");
+ }
+ return p;
+ }
+
+ private static PackageInfo createPackageInfo(
+ String packageName, boolean enabled, boolean valid, Signature[] signatures) {
+ PackageInfo p = createPackageInfo(packageName, enabled, valid);
+ p.signatures = signatures;
+ return p;
+ }
+
+
+ // ****************
+ // Tests
+ // ****************
+
+
+ public void testWithSinglePackage() {
+ String testPackageName = "test.package.name";
+ checkCertainPackageUsedAfterWebViewPreparation(testPackageName,
+ new WebViewProviderInfo[] {
+ new WebViewProviderInfo(testPackageName, "",
+ true /*default available*/, false /* fallback */, null)});
+ }
+
+ public void testDefaultPackageUsedOverNonDefault() {
+ String defaultPackage = "defaultPackage";
+ String nonDefaultPackage = "nonDefaultPackage";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(nonDefaultPackage, "", false, false, null),
+ new WebViewProviderInfo(defaultPackage, "", true, false, null)};
+ checkCertainPackageUsedAfterWebViewPreparation(defaultPackage, packages);
+ }
+
+ public void testSeveralRelros() {
+ String singlePackage = "singlePackage";
+ checkCertainPackageUsedAfterWebViewPreparation(
+ singlePackage,
+ new WebViewProviderInfo[] {
+ new WebViewProviderInfo(singlePackage, "", true /*def av*/, false, null)},
+ 2);
+ }
+
+ // Ensure that package with valid signatures is chosen rather than package with invalid
+ // signatures.
+ public void testWithSignatures() {
+ String validPackage = "valid package";
+ String invalidPackage = "invalid package";
+
+ Signature validSignature = new Signature("11");
+ Signature invalidExpectedSignature = new Signature("22");
+ Signature invalidPackageSignature = new Signature("33");
+
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(invalidPackage, "", true, false, new String[]{
+ Base64.encodeToString(
+ invalidExpectedSignature.toByteArray(), Base64.DEFAULT)}),
+ new WebViewProviderInfo(validPackage, "", true, false, new String[]{
+ Base64.encodeToString(
+ validSignature.toByteArray(), Base64.DEFAULT)})
+ };
+ setupWithPackages(packages, true /* fallback logic enabled */, 1 /* numRelros */,
+ false /* isDebuggable */);
+ mTestSystemImpl.setPackageInfo(createPackageInfo(invalidPackage, true /* enabled */,
+ true /* valid */, new Signature[]{invalidPackageSignature}));
+ mTestSystemImpl.setPackageInfo(createPackageInfo(validPackage, true /* enabled */,
+ true /* valid */, new Signature[]{validSignature}));
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(validPackage)));
+
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+
+ WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+ assertEquals(validPackage, response.packageInfo.packageName);
+
+ WebViewProviderInfo[] validPackages = mWebViewUpdateServiceImpl.getValidWebViewPackages();
+ assertEquals(1, validPackages.length);
+ assertEquals(validPackage, validPackages[0].packageName);
+ }
+
+ public void testFailWaitingForRelro() {
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo("packagename", "", true, true, null)};
+ setupWithPackages(packages);
+ setEnabledAndValidPackageInfos(packages);
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(packages[0].packageName)));
+
+ // Never call notifyRelroCreation()
+
+ WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO, response.status);
+ }
+
+ public void testFailListingEmptyWebviewPackages() {
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[0];
+ setupWithPackages(packages);
+ setEnabledAndValidPackageInfos(packages);
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
+ Matchers.anyObject());
+
+ WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
+ }
+
+ public void testFailListingInvalidWebviewPackage() {
+ WebViewProviderInfo wpi = new WebViewProviderInfo("", "", true, true, null);
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {wpi};
+ setupWithPackages(packages);
+ mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName, true, false));
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
+ }
+
+ // Test that switching provider using changeProviderAndSetting works.
+ public void testSwitchingProvider() {
+ String firstPackage = "first";
+ String secondPackage = "second";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(firstPackage, "", true, false, null),
+ new WebViewProviderInfo(secondPackage, "", true, false, null)};
+ checkSwitchingProvider(packages, firstPackage, secondPackage);
+ }
+
+ public void testSwitchingProviderToNonDefault() {
+ String defaultPackage = "defaultPackage";
+ String nonDefaultPackage = "nonDefaultPackage";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(defaultPackage, "", true, false, null),
+ new WebViewProviderInfo(nonDefaultPackage, "", false, false, null)};
+ checkSwitchingProvider(packages, defaultPackage, nonDefaultPackage);
+ }
+
+ private void checkSwitchingProvider(WebViewProviderInfo[] packages, String initialPackage,
+ String finalPackage) {
+ checkCertainPackageUsedAfterWebViewPreparation(initialPackage, packages);
+
+ mWebViewUpdateServiceImpl.changeProviderAndSetting(finalPackage);
+
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(finalPackage)));
+
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+
+ WebViewProviderResponse secondResponse = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_SUCCESS, secondResponse.status);
+ assertEquals(finalPackage, secondResponse.packageInfo.packageName);
+
+ Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(initialPackage));
+ }
+
+ // Change provider during relro creation by using changeProviderAndSetting
+ public void testSwitchingProviderDuringRelroCreation() {
+ checkChangingProviderDuringRelroCreation(true);
+ }
+
+ // Change provider during relro creation by enabling a provider
+ public void testChangingProviderThroughEnablingDuringRelroCreation() {
+ checkChangingProviderDuringRelroCreation(false);
+ }
+
+ private void checkChangingProviderDuringRelroCreation(boolean settingsChange) {
+ String firstPackage = "first";
+ String secondPackage = "second";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(firstPackage, "", true, false, null),
+ new WebViewProviderInfo(secondPackage, "", true, false, null)};
+ setupWithPackages(packages);
+ if (settingsChange) {
+ // Have all packages be enabled, so that we can change provider however we want to
+ setEnabledAndValidPackageInfos(packages);
+ } else {
+ // Have all packages be disabled so that we can change one to enabled later
+ for(WebViewProviderInfo wpi : packages) {
+ mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName,
+ false /* enabled */, true /* valid */));
+ }
+ }
+
+ CountDownLatch countdown = new CountDownLatch(1);
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
+
+ assertEquals(firstPackage, mWebViewUpdateServiceImpl.getCurrentWebViewPackageName());
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ WebViewProviderResponse threadResponse =
+ mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_SUCCESS, threadResponse.status);
+ assertEquals(secondPackage, threadResponse.packageInfo.packageName);
+ // Verify that we killed the first package
+ Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage));
+ countdown.countDown();
+ }
+ }).start();
+ try {
+ Thread.sleep(1000); // Let the new thread run / be blocked
+ } catch (InterruptedException e) {
+ }
+
+ if (settingsChange) {
+ mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
+ } else {
+ // Switch provider by enabling the second one
+ mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
+ true /* valid */));
+ mWebViewUpdateServiceImpl.packageStateChanged(
+ secondPackage, WebViewUpdateService.PACKAGE_CHANGED);
+ }
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+ // first package done, should start on second
+
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(secondPackage)));
+
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+ // second package done, the other thread should now be unblocked
+ try {
+ countdown.await();
+ } catch (InterruptedException e) {
+ }
+ }
+
+ public void testRunFallbackLogicIfEnabled() {
+ checkFallbackLogicBeingRun(true);
+ }
+
+ public void testDontRunFallbackLogicIfDisabled() {
+ checkFallbackLogicBeingRun(false);
+ }
+
+ private void checkFallbackLogicBeingRun(boolean fallbackLogicEnabled) {
+ String primaryPackage = "primary";
+ String fallbackPackage = "fallback";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(
+ primaryPackage, "", true /* default available */, false /* fallback */, null),
+ new WebViewProviderInfo(
+ fallbackPackage, "", true /* default available */, true /* fallback */, null)};
+ setupWithPackages(packages, fallbackLogicEnabled);
+ setEnabledAndValidPackageInfos(packages);
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ // Verify that we disable the fallback package if fallback logic enabled, and don't disable
+ // the fallback package if that logic is disabled
+ if (fallbackLogicEnabled) {
+ Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
+ Matchers.anyObject(), Mockito.eq(fallbackPackage));
+ } else {
+ Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
+ Matchers.anyObject(), Matchers.anyObject());
+ }
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(primaryPackage)));
+
+ // Enable fallback package
+ mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
+ true /* valid */));
+ mWebViewUpdateServiceImpl.packageStateChanged(
+ fallbackPackage, WebViewUpdateService.PACKAGE_CHANGED);
+
+ if (fallbackLogicEnabled) {
+ // Check that we have now disabled the fallback package twice
+ Mockito.verify(mTestSystemImpl, Mockito.times(2)).uninstallAndDisablePackageForAllUsers(
+ Matchers.anyObject(), Mockito.eq(fallbackPackage));
+ } else {
+ // Check that we still haven't disabled any package
+ Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
+ Matchers.anyObject(), Matchers.anyObject());
+ }
+ }
+
+ /**
+ * Scenario for installing primary package when fallback enabled.
+ * 1. Start with only fallback installed
+ * 2. Install non-fallback
+ * 3. Fallback should be disabled
+ */
+ public void testInstallingNonFallbackPackage() {
+ String primaryPackage = "primary";
+ String fallbackPackage = "fallback";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(
+ primaryPackage, "", true /* default available */, false /* fallback */, null),
+ new WebViewProviderInfo(
+ fallbackPackage, "", true /* default available */, true /* fallback */, null)};
+ setupWithPackages(packages, true /* isFallbackLogicEnabled */);
+ mTestSystemImpl.setPackageInfo(
+ createPackageInfo(fallbackPackage, true /* enabled */ , true /* valid */));
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
+ Matchers.anyObject(), Matchers.anyObject());
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(fallbackPackage)));
+
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+
+ WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+ assertEquals(fallbackPackage, response.packageInfo.packageName);
+
+ // Install primary package
+ mTestSystemImpl.setPackageInfo(
+ createPackageInfo(primaryPackage, true /* enabled */ , true /* valid */));
+ mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+ WebViewUpdateService.PACKAGE_ADDED);
+
+ // Verify fallback disabled and primary package used as provider
+ Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
+ Matchers.anyObject(), Mockito.eq(fallbackPackage));
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(primaryPackage)));
+
+ // Finish the webview preparation and ensure primary package used and fallback killed
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+ response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+ assertEquals(primaryPackage, response.packageInfo.packageName);
+ Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(fallbackPackage));
+ }
+
+ public void testFallbackChangesEnabledState() {
+ String primaryPackage = "primary";
+ String fallbackPackage = "fallback";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(
+ primaryPackage, "", true /* default available */, false /* fallback */, null),
+ new WebViewProviderInfo(
+ fallbackPackage, "", true /* default available */, true /* fallback */, null)};
+ setupWithPackages(packages, true /* fallbackLogicEnabled */);
+ setEnabledAndValidPackageInfos(packages);
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ // Verify fallback disabled at boot when primary package enabled
+ Mockito.verify(mTestSystemImpl).enablePackageForUser(
+ Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
+ Matchers.anyInt());
+
+ mTestSystemImpl.setPackageInfo(
+ createPackageInfo(primaryPackage, false /* enabled */, true /* valid */));
+ mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+ WebViewUpdateService.PACKAGE_CHANGED);
+
+ // Verify fallback becomes enabled when primary package becomes disabled
+ Mockito.verify(mTestSystemImpl).enablePackageForUser(
+ Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */,
+ Matchers.anyInt());
+
+ mTestSystemImpl.setPackageInfo(
+ createPackageInfo(primaryPackage, true /* enabled */, true /* valid */));
+ mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+ WebViewUpdateService.PACKAGE_CHANGED);
+
+ // Verify fallback is disabled a second time when primary package becomes enabled
+ Mockito.verify(mTestSystemImpl, Mockito.times(2)).enablePackageForUser(
+ Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
+ Matchers.anyInt());
+ }
+
+ public void testAddUserWhenFallbackLogicEnabled() {
+ checkAddingNewUser(true);
+ }
+
+ public void testAddUserWhenFallbackLogicDisabled() {
+ checkAddingNewUser(false);
+ }
+
+ public void checkAddingNewUser(boolean fallbackLogicEnabled) {
+ String primaryPackage = "primary";
+ String fallbackPackage = "fallback";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(
+ primaryPackage, "", true /* default available */, false /* fallback */, null),
+ new WebViewProviderInfo(
+ fallbackPackage, "", true /* default available */, true /* fallback */, null)};
+ setupWithPackages(packages, fallbackLogicEnabled);
+ setEnabledAndValidPackageInfos(packages);
+ int newUser = 100;
+ mWebViewUpdateServiceImpl.handleNewUser(newUser);
+ if (fallbackLogicEnabled) {
+ // Verify fallback package becomes disabled for new user
+ Mockito.verify(mTestSystemImpl).enablePackageForUser(
+ Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
+ Mockito.eq(newUser));
+ } else {
+ // Verify that we don't disable fallback for new user
+ Mockito.verify(mTestSystemImpl, Mockito.never()).enablePackageForUser(
+ Mockito.anyObject(), Matchers.anyBoolean() /* enable */,
+ Matchers.anyInt() /* user */);
+ }
+ }
+
+ /**
+ * Timing dependent test where we verify that the list of valid webview packages becoming empty
+ * at a certain point doesn't crash us or break our state.
+ */
+ public void testNotifyRelroDoesntCrashIfNoPackages() {
+ String firstPackage = "first";
+ String secondPackage = "second";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(firstPackage, "", true /* default available */,
+ false /* fallback */, null),
+ new WebViewProviderInfo(secondPackage, "", true /* default available */,
+ false /* fallback */, null)};
+ setupWithPackages(packages);
+ // Add (enabled and valid) package infos for each provider
+ setEnabledAndValidPackageInfos(packages);
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
+
+ mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
+
+ // Make packages invalid to cause exception to be thrown
+ mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
+ false /* valid */));
+ mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
+ false /* valid */));
+
+ // This shouldn't throw an exception!
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+
+ WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
+
+ // Now make a package valid again and verify that we can switch back to that
+ mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
+ true /* valid */));
+
+ mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
+ WebViewUpdateService.PACKAGE_ADDED);
+
+ // Second time we call onWebViewProviderChanged for firstPackage
+ Mockito.verify(mTestSystemImpl, Mockito.times(2)).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
+
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+
+ response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+ assertEquals(firstPackage, response.packageInfo.packageName);
+ }
+
+ // TODO (gsennton) add more tests for ensuring killPackageDependents is called / not called
+}
diff --git a/services/tests/shortcutmanagerutils/Android.mk b/services/tests/shortcutmanagerutils/Android.mk
new file mode 100644
index 0000000..701e058
--- /dev/null
+++ b/services/tests/shortcutmanagerutils/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ mockito-target
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := ShortcutManagerTestUtils
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
new file mode 100644
index 0000000..d09b62c
--- /dev/null
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm.shortcutmanagertest;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyList;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.BaseBundle;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.test.MoreAsserts;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.mockito.Mockito;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.BooleanSupplier;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class ShortcutManagerTestUtils {
+ private static final String TAG = "ShortcutManagerUtils";
+
+ private static final boolean ENABLE_DUMPSYS = true; // DO NOT SUBMIT WITH true
+
+ private static final int STANDARD_TIMEOUT_SEC = 5;
+
+ private ShortcutManagerTestUtils() {
+ }
+
+ private static List<String> readAll(ParcelFileDescriptor pfd) {
+ try {
+ try {
+ final ArrayList<String> ret = new ArrayList<>();
+ try (BufferedReader r = new BufferedReader(
+ new FileReader(pfd.getFileDescriptor()))) {
+ String line;
+ while ((line = r.readLine()) != null) {
+ ret.add(line);
+ }
+ r.readLine();
+ }
+ return ret;
+ } finally {
+ pfd.close();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static String concatResult(List<String> result) {
+ final StringBuilder sb = new StringBuilder();
+ for (String s : result) {
+ sb.append(s);
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+
+ private static List<String> runCommand(Instrumentation instrumentation, String command) {
+ return runCommand(instrumentation, command, null);
+ }
+ private static List<String> runCommand(Instrumentation instrumentation, String command,
+ Predicate<List<String>> resultAsserter) {
+ Log.d(TAG, "Running command: " + command);
+ final List<String> result;
+ try {
+ result = readAll(
+ instrumentation.getUiAutomation().executeShellCommand(command));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ if (resultAsserter != null && !resultAsserter.test(result)) {
+ fail("Command '" + command + "' failed, output was:\n" + concatResult(result));
+ }
+ return result;
+ }
+
+ private static void runCommandForNoOutput(Instrumentation instrumentation, String command) {
+ runCommand(instrumentation, command, result -> result.size() == 0);
+ }
+
+ private static List<String> runShortcutCommand(Instrumentation instrumentation, String command,
+ Predicate<List<String>> resultAsserter) {
+ return runCommand(instrumentation, "cmd shortcut " + command, resultAsserter);
+ }
+
+ public static List<String> runShortcutCommandForSuccess(Instrumentation instrumentation,
+ String command) {
+ return runShortcutCommand(instrumentation, command, result -> result.contains("Success"));
+ }
+
+ public static String getDefaultLauncher(Instrumentation instrumentation) {
+ final String PREFIX = "Launcher: ComponentInfo{";
+ final String POSTFIX = "}";
+ final List<String> result = runShortcutCommandForSuccess(
+ instrumentation, "get-default-launcher");
+ for (String s : result) {
+ if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) {
+ return s.substring(PREFIX.length(), s.length() - POSTFIX.length());
+ }
+ }
+ fail("Default launcher not found");
+ return null;
+ }
+
+ public static void setDefaultLauncher(Instrumentation instrumentation, String component) {
+ runCommandForNoOutput(instrumentation, "cmd package set-home-activity " + component);
+ }
+
+ public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
+ setDefaultLauncher(instrumentation, packageContext.getPackageName()
+ + "/android.content.pm.cts.shortcutmanager.packages.Launcher");
+ }
+
+ public static void overrideConfig(Instrumentation instrumentation, String config) {
+ runShortcutCommandForSuccess(instrumentation, "override-config " + config);
+ }
+
+ public static void resetConfig(Instrumentation instrumentation) {
+ runShortcutCommandForSuccess(instrumentation, "reset-config");
+ }
+
+ public static void resetThrottling(Instrumentation instrumentation) {
+ runShortcutCommandForSuccess(instrumentation, "reset-throttling");
+ }
+
+ public static void resetAllThrottling(Instrumentation instrumentation) {
+ runShortcutCommandForSuccess(instrumentation, "reset-all-throttling");
+ }
+
+ public static void clearShortcuts(Instrumentation instrumentation, int userId,
+ String packageName) {
+ runShortcutCommandForSuccess(instrumentation, "clear-shortcuts "
+ + " --user " + userId + " " + packageName);
+ }
+
+ public static void dumpsysShortcut(Instrumentation instrumentation) {
+ if (!ENABLE_DUMPSYS) {
+ return;
+ }
+ for (String s : runCommand(instrumentation, "dumpsys shortcut")) {
+ Log.e(TAG, s);
+ }
+ }
+
+ public static Bundle makeBundle(Object... keysAndValues) {
+ assertTrue((keysAndValues.length % 2) == 0);
+
+ if (keysAndValues.length == 0) {
+ return null;
+ }
+ final Bundle ret = new Bundle();
+
+ for (int i = keysAndValues.length - 2; i >= 0; i -= 2) {
+ final String key = keysAndValues[i].toString();
+ final Object value = keysAndValues[i + 1];
+
+ if (value == null) {
+ ret.putString(key, null);
+ } else if (value instanceof Integer) {
+ ret.putInt(key, (Integer) value);
+ } else if (value instanceof String) {
+ ret.putString(key, (String) value);
+ } else if (value instanceof Bundle) {
+ ret.putBundle(key, (Bundle) value);
+ } else {
+ fail("Type not supported yet: " + value.getClass().getName());
+ }
+ }
+ return ret;
+ }
+
+ public static <T> List<T> list(T... array) {
+ return Arrays.asList(array);
+ }
+
+ public static <T> Set<T> hashSet(Set<T> in) {
+ return new HashSet<T>(in);
+ }
+
+ public static <T> Set<T> set(T... values) {
+ return set(v -> v, values);
+ }
+
+ public static <T, V> Set<T> set(Function<V, T> converter, V... values) {
+ return set(converter, Arrays.asList(values));
+ }
+
+ public static <T, V> Set<T> set(Function<V, T> converter, List<V> values) {
+ final HashSet<T> ret = new HashSet<>();
+ for (V v : values) {
+ ret.add(converter.apply(v));
+ }
+ return ret;
+ }
+
+ public static void resetAll(Collection<?> mocks) {
+ for (Object o : mocks) {
+ reset(o);
+ }
+ }
+ public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
+ String expectedExceptionMessageRegex, Runnable r) {
+ assertExpectException("", expectedExceptionType, expectedExceptionMessageRegex, r);
+ }
+
+ public static void assertDynamicShortcutCountExceeded(Runnable r) {
+ assertExpectException(IllegalArgumentException.class,
+ "Max number of dynamic shortcuts exceeded", r);
+ }
+
+ public static void assertExpectException(String message,
+ Class<? extends Throwable> expectedExceptionType,
+ String expectedExceptionMessageRegex, Runnable r) {
+ try {
+ r.run();
+ Assert.fail("Expected exception type " + expectedExceptionType.getName()
+ + " was not thrown (message=" + message + ")");
+ } catch (Throwable e) {
+ Assert.assertTrue(
+ "Expected exception type was " + expectedExceptionType.getName()
+ + " but caught " + e + " (message=" + message + ")",
+ expectedExceptionType.isAssignableFrom(e.getClass()));
+ if (expectedExceptionMessageRegex != null) {
+ MoreAsserts.assertContainsRegex(expectedExceptionMessageRegex, e.getMessage());
+ }
+ }
+ }
+
+ public static List<ShortcutInfo> assertShortcutIds(List<ShortcutInfo> actualShortcuts,
+ String... expectedIds) {
+ final HashSet<String> expected = new HashSet<>(list(expectedIds));
+ final HashSet<String> actual = new HashSet<>();
+ for (ShortcutInfo s : actualShortcuts) {
+ actual.add(s.getId());
+ }
+
+ // Compare the sets.
+ assertEquals(expected, actual);
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllHaveIntents(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertNotNull("ID " + s.getId(), s.getIntent());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllNotHaveIntents(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertNull("ID " + s.getId(), s.getIntent());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllHaveTitle(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertNotNull("ID " + s.getId(), s.getTitle());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllNotHaveTitle(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertNull("ID " + s.getId(), s.getTitle());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllHaveIconResId(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId() + " not have icon res ID", s.hasIconResource());
+ assertFalse("ID " + s.getId() + " shouldn't have icon FD", s.hasIconFile());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllHaveIconFile(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertFalse("ID " + s.getId() + " shouldn't have icon res ID", s.hasIconResource());
+ assertTrue("ID " + s.getId() + " not have icon FD", s.hasIconFile());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllHaveIcon(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId() + " has no icon ", s.hasIconFile() || s.hasIconResource());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllKeyFieldsOnly(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.hasKeyFieldsOnly());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllNotKeyFieldsOnly(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertFalse("ID " + s.getId(), s.hasKeyFieldsOnly());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllDynamic(List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.isDynamic());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllPinned(List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.isPinned());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllDynamicOrPinned(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.isDynamic() || s.isPinned());
+ }
+ return actualShortcuts;
+ }
+
+ public static void assertDynamicOnly(ShortcutInfo si) {
+ assertTrue(si.isDynamic());
+ assertFalse(si.isPinned());
+ }
+
+ public static void assertPinnedOnly(ShortcutInfo si) {
+ assertFalse(si.isDynamic());
+ assertTrue(si.isPinned());
+ }
+
+ public static void assertDynamicAndPinned(ShortcutInfo si) {
+ assertTrue(si.isDynamic());
+ assertTrue(si.isPinned());
+ }
+
+ public static void assertBitmapSize(int expectedWidth, int expectedHeight, Bitmap bitmap) {
+ assertEquals("width", expectedWidth, bitmap.getWidth());
+ assertEquals("height", expectedHeight, bitmap.getHeight());
+ }
+
+ public static <T> void assertAllUnique(Collection<T> list) {
+ final Set<Object> set = new HashSet<>();
+ for (T item : list) {
+ if (set.contains(item)) {
+ fail("Duplicate item found: " + item + " (in the list: " + list + ")");
+ }
+ set.add(item);
+ }
+ }
+
+ public static Bitmap pfdToBitmap(ParcelFileDescriptor pfd) {
+ assertNotNull(pfd);
+ try {
+ try {
+ return BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
+ } finally {
+ pfd.close();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void assertBundleEmpty(BaseBundle b) {
+ assertTrue(b == null || b.size() == 0);
+ }
+
+ public static void assertCallbackNotReceived(LauncherApps.Callback mock) {
+ verify(mock, times(0)).onShortcutsChanged(anyString(), anyList(),
+ any(UserHandle.class));
+ }
+
+ public static void assertCallbackReceived(LauncherApps.Callback mock,
+ UserHandle user, String packageName, String... ids) {
+ verify(mock).onShortcutsChanged(eq(packageName), checkShortcutIds(ids),
+ eq(user));
+ }
+
+ public static boolean checkAssertSuccess(Runnable r) {
+ try {
+ r.run();
+ return true;
+ } catch (AssertionError e) {
+ return false;
+ }
+ }
+
+ public static <T> T checkArgument(Predicate<T> checker, String description,
+ List<T> matchedCaptor) {
+ final Matcher<T> m = new BaseMatcher<T>() {
+ @Override
+ public boolean matches(Object item) {
+ if (item == null) {
+ return false;
+ }
+ final T value = (T) item;
+ if (!checker.test(value)) {
+ return false;
+ }
+
+ if (matchedCaptor != null) {
+ matchedCaptor.add(value);
+ }
+ return true;
+ }
+
+ @Override
+ public void describeTo(Description d) {
+ d.appendText(description);
+ }
+ };
+ return Mockito.argThat(m);
+ }
+
+ public static List<ShortcutInfo> checkShortcutIds(String... ids) {
+ return checkArgument((List<ShortcutInfo> list) -> {
+ final Set<String> actualSet = set(si -> si.getId(), list);
+ return actualSet.equals(set(ids));
+
+ }, "Shortcut IDs=[" + Arrays.toString(ids) + "]", null);
+ }
+
+ public static void waitUntil(String message, BooleanSupplier condition) {
+ waitUntil(message, condition, STANDARD_TIMEOUT_SEC);
+ }
+
+ public static void waitUntil(String message, BooleanSupplier condition, int timeoutSeconds) {
+ final long timeout = System.currentTimeMillis() + (timeoutSeconds * 1000L);
+ while (System.currentTimeMillis() < timeout) {
+ if (condition.getAsBoolean()) {
+ return;
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ fail("Timed out for: " + message);
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 0aeb96f..ecfeff9 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -152,6 +152,7 @@
private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
mPackageAccessListeners = new ArrayList<>();
+ private boolean mHaveCarrierPrivilegedApps;
private List<String> mCarrierPrivilegedApps;
public UsageStatsService(Context context) {
@@ -931,11 +932,14 @@
private boolean isCarrierApp(String packageName) {
synchronized (mLock) {
- if (mCarrierPrivilegedApps == null) {
+ if (!mHaveCarrierPrivilegedApps) {
fetchCarrierPrivilegedAppsLocked();
}
+ if (mCarrierPrivilegedApps != null) {
+ return mCarrierPrivilegedApps.contains(packageName);
+ }
+ return false;
}
- return mCarrierPrivilegedApps.contains(packageName);
}
void clearCarrierPrivilegedApps() {
@@ -943,6 +947,7 @@
Slog.i(TAG, "Clearing carrier privileged apps list");
}
synchronized (mLock) {
+ mHaveCarrierPrivilegedApps = false;
mCarrierPrivilegedApps = null; // Need to be refetched.
}
}
@@ -951,6 +956,7 @@
TelephonyManager telephonyManager =
getContext().getSystemService(TelephonyManager.class);
mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges();
+ mHaveCarrierPrivilegedApps = true;
if (DEBUG) {
Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
}
@@ -1024,7 +1030,8 @@
}
pw.println();
- pw.println("Carrier privileged apps: " + mCarrierPrivilegedApps);
+ pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
+ + "): " + mCarrierPrivilegedApps);
pw.println();
pw.println("Settings:");
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index adc7c21..c18c13c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -426,6 +426,13 @@
public static final String KEY_CARRIER_METERED_APN_TYPES_STRINGS =
"carrier_metered_apn_types_strings";
+ /**
+ * CDMA carrier ERI (Enhanced Roaming Indicator) file name
+ * @hide
+ */
+ public static final String KEY_CARRIER_ERI_FILE_NAME_STRING =
+ "carrier_eri_file_name_string";
+
/* The following 3 fields are related to carrier visual voicemail. */
/**
@@ -705,6 +712,7 @@
"max_retries=3, 5000, 5000, 5000");
sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 20000);
sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 3000);
+ sDefaults.putString(KEY_CARRIER_ERI_FILE_NAME_STRING, "eri.xml");
sDefaults.putInt(KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT, 7200);
sDefaults.putStringArray(KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{"default", "mms", "dun", "supl"});
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 865af78..638b286 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -717,16 +717,11 @@
*/
/** {@hide} */
public String getDeviceSoftwareVersion(int slotId) {
- // FIXME methods taking slot id should not use subscription, instead us Uicc directly
- int[] subId = SubscriptionManager.getSubId(slotId);
- if (subId == null || subId.length == 0) {
- return null;
- }
+ ITelephony telephony = getITelephony();
+ if (telephony == null) return null;
+
try {
- IPhoneSubInfo info = getSubscriberInfo();
- if (info == null)
- return null;
- return info.getDeviceSvnUsingSubId(subId[0], mContext.getOpPackageName());
+ return telephony.getDeviceSoftwareVersionForSlot(slotId, getOpPackageName());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -798,12 +793,11 @@
*/
/** {@hide} */
public String getImei(int slotId) {
- int[] subId = SubscriptionManager.getSubId(slotId);
+ ITelephony telephony = getITelephony();
+ if (telephony == null) return null;
+
try {
- IPhoneSubInfo info = getSubscriberInfo();
- if (info == null)
- return null;
- return info.getImeiForSubscriber(subId[0], mContext.getOpPackageName());
+ return telephony.getImeiForSlot(slotId, getOpPackageName());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -1014,22 +1008,32 @@
} else {
phoneId = SubscriptionManager.getPhoneId(subId);
}
+
+ return getCurrentPhoneTypeForSlot(phoneId);
+ }
+
+ /**
+ * See getCurrentPhoneType.
+ *
+ * @hide
+ */
+ public int getCurrentPhoneTypeForSlot(int slotId) {
try{
ITelephony telephony = getITelephony();
- if (telephony != null && subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- return telephony.getActivePhoneTypeForSubscriber(subId);
+ if (telephony != null) {
+ return telephony.getActivePhoneTypeForSlot(slotId);
} else {
// This can happen when the ITelephony interface is not up yet.
- return getPhoneTypeFromProperty(phoneId);
+ return getPhoneTypeFromProperty(slotId);
}
} catch (RemoteException ex) {
// This shouldn't happen in the normal case, as a backup we
// read from the system property.
- return getPhoneTypeFromProperty(phoneId);
+ return getPhoneTypeFromProperty(slotId);
} catch (NullPointerException ex) {
// This shouldn't happen in the normal case, as a backup we
// read from the system property.
- return getPhoneTypeFromProperty(phoneId);
+ return getPhoneTypeFromProperty(slotId);
}
}
@@ -2555,20 +2559,31 @@
* @param subId whose call state is returned
*/
public int getCallState(int subId) {
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+ return getCallStateForSlot(phoneId);
+ }
+
+ /**
+ * See getCallState.
+ *
+ * @hide
+ */
+ public int getCallStateForSlot(int slotId) {
try {
ITelephony telephony = getITelephony();
if (telephony == null)
return CALL_STATE_IDLE;
- return telephony.getCallStateForSubscriber(subId);
+ return telephony.getCallStateForSlot(slotId);
} catch (RemoteException ex) {
// the phone process is restarting.
return CALL_STATE_IDLE;
} catch (NullPointerException ex) {
// the phone process is restarting.
return CALL_STATE_IDLE;
- }
+ }
}
+
/** Data connection activity: No traffic. */
public static final int DATA_ACTIVITY_NONE = 0x00000000;
/** Data connection activity: Currently receiving IP PPP traffic. */
diff --git a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
new file mode 100644
index 0000000..ca7354f
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.telephony;
+
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.RemoteException;
+import android.telephony.TelephonyManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utilities for handling carrier applications.
+ * @hide
+ */
+public final class CarrierAppUtils {
+ private static final String TAG = "CarrierAppUtils";
+
+ private static final boolean DEBUG = false; // STOPSHIP if true
+
+ private CarrierAppUtils() {}
+
+ /**
+ * Handle preinstalled carrier apps which should be disabled until a matching SIM is inserted.
+ *
+ * Evaluates the list of applications in config_disabledUntilUsedPreinstalledCarrierApps. We
+ * want to disable each such application which is present on the system image until the user
+ * inserts a SIM which causes that application to gain carrier privilege (indicating a "match"),
+ * without interfering with the user if they opt to enable/disable the app explicitly.
+ *
+ * So, for each such app, we either disable until used IFF the app is not carrier privileged AND
+ * in the default state (e.g. not explicitly DISABLED/DISABLED_BY_USER/ENABLED), or we enable if
+ * the app is carrier privileged and in either the default state or DISABLED_UNTIL_USED.
+ *
+ * When enabling a carrier app we also grant it default permissions.
+ *
+ * This method is idempotent and is safe to be called at any time; it should be called once at
+ * system startup prior to any application running, as well as any time the set of carrier
+ * privileged apps may have changed.
+ */
+ public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage,
+ IPackageManager packageManager, TelephonyManager telephonyManager, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "disableCarrierAppsUntilPrivileged");
+ }
+ String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
+ com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+ disableCarrierAppsUntilPrivileged(callingPackage, packageManager, telephonyManager, userId,
+ systemCarrierAppsDisabledUntilUsed);
+ }
+
+ /**
+ * Like {@link #disableCarrierAppsUntilPrivileged(String, IPackageManager, TelephonyManager,
+ * int)}, but assumes that no carrier apps have carrier privileges.
+ *
+ * This prevents a potential race condition on first boot - since the app's default state is
+ * enabled, we will initially disable it when the telephony stack is first initialized as it has
+ * not yet read the carrier privilege rules. However, since telephony is initialized later on
+ * late in boot, the app being disabled may have already been started in response to certain
+ * broadcasts. The app will continue to run (briefly) after being disabled, before the Package
+ * Manager can kill it, and this can lead to crashes as the app is in an unexpected state.
+ */
+ public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage,
+ IPackageManager packageManager, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "disableCarrierAppsUntilPrivileged");
+ }
+ String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
+ com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+ disableCarrierAppsUntilPrivileged(callingPackage, packageManager,
+ null /* telephonyManager */, userId, systemCarrierAppsDisabledUntilUsed);
+ }
+
+ // Must be public b/c framework unit tests can't access package-private methods.
+ @VisibleForTesting
+ public static void disableCarrierAppsUntilPrivileged(String callingPackage,
+ IPackageManager packageManager, @Nullable TelephonyManager telephonyManager, int userId,
+ String[] systemCarrierAppsDisabledUntilUsed) {
+ List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(packageManager,
+ userId, systemCarrierAppsDisabledUntilUsed);
+ if (candidates == null || candidates.isEmpty()) {
+ return;
+ }
+
+ List<String> enabledCarrierPackages = new ArrayList<>();
+
+ try {
+ for (ApplicationInfo ai : candidates) {
+ String packageName = ai.packageName;
+ boolean hasPrivileges = telephonyManager != null &&
+ telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
+ TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+
+ // Only update enabled state for the app on /system. Once it has been updated we
+ // shouldn't touch it.
+ if (!ai.isUpdatedSystemApp()) {
+ if (hasPrivileges
+ && (ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ || ai.enabledSetting ==
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
+ Slog.i(TAG, "Update state(" + packageName + "): ENABLED for user "
+ + userId);
+ packageManager.setApplicationEnabledSetting(packageName,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP, userId, callingPackage);
+ } else if (!hasPrivileges
+ && ai.enabledSetting ==
+ PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+ Slog.i(TAG, "Update state(" + packageName
+ + "): DISABLED_UNTIL_USED for user " + userId);
+ packageManager.setApplicationEnabledSetting(packageName,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0,
+ userId, callingPackage);
+ }
+ }
+
+ // Always re-grant default permissions to carrier apps w/ privileges.
+ if (hasPrivileges) {
+ enabledCarrierPackages.add(ai.packageName);
+ }
+ }
+
+ if (!enabledCarrierPackages.isEmpty()) {
+ // Since we enabled at least one app, ensure we grant default permissions to those
+ // apps.
+ String[] packageNames = new String[enabledCarrierPackages.size()];
+ enabledCarrierPackages.toArray(packageNames);
+ packageManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not reach PackageManager", e);
+ }
+ }
+
+ /**
+ * Returns the list of "default" carrier apps.
+ *
+ * This is the subset of apps returned by
+ * {@link #getDefaultCarrierAppCandidates(IPackageManager, int)} which currently have carrier
+ * privileges per the SIM(s) inserted in the device.
+ */
+ public static List<ApplicationInfo> getDefaultCarrierApps(IPackageManager packageManager,
+ TelephonyManager telephonyManager, int userId) {
+ // Get all system apps from the default list.
+ List<ApplicationInfo> candidates = getDefaultCarrierAppCandidates(packageManager, userId);
+ if (candidates == null || candidates.isEmpty()) {
+ return null;
+ }
+
+ // Filter out apps without carrier privileges.
+ // Iterate from the end to avoid creating an Iterator object and because we will be removing
+ // elements from the list as we pass through it.
+ for (int i = candidates.size() - 1; i >= 0; i--) {
+ ApplicationInfo ai = candidates.get(i);
+ String packageName = ai.packageName;
+ boolean hasPrivileges =
+ telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
+ TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ if (!hasPrivileges) {
+ candidates.remove(i);
+ }
+ }
+
+ return candidates;
+ }
+
+ /**
+ * Returns the list of "default" carrier app candidates.
+ *
+ * These are the apps subject to the hiding/showing logic in
+ * {@link CarrierAppUtils#disableCarrierAppsUntilPrivileged(String, IPackageManager,
+ * TelephonyManager, int)}, as well as the apps which should have default permissions granted,
+ * when a matching SIM is inserted.
+ *
+ * Whether or not the app is actually considered a default app depends on whether the app has
+ * carrier privileges as determined by the SIMs in the device.
+ */
+ public static List<ApplicationInfo> getDefaultCarrierAppCandidates(
+ IPackageManager packageManager, int userId) {
+ String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
+ com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+ return getDefaultCarrierAppCandidatesHelper(packageManager, userId,
+ systemCarrierAppsDisabledUntilUsed);
+ }
+
+ private static List<ApplicationInfo> getDefaultCarrierAppCandidatesHelper(
+ IPackageManager packageManager, int userId,
+ String[] systemCarrierAppsDisabledUntilUsed) {
+ if (systemCarrierAppsDisabledUntilUsed == null
+ || systemCarrierAppsDisabledUntilUsed.length == 0) {
+ return null;
+ }
+ List<ApplicationInfo> apps = null;
+ try {
+ apps = new ArrayList<>(systemCarrierAppsDisabledUntilUsed.length);
+ for (String packageName : systemCarrierAppsDisabledUntilUsed) {
+ ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, userId);
+ if (ai == null) {
+ // No app found for packageName
+ continue;
+ }
+ if (!ai.isSystemApp()) {
+ continue;
+ }
+ apps.add(ai);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not reach PackageManager", e);
+ }
+ return apps;
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index fcb967f..ccabace 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -102,6 +102,7 @@
public static final int CMD_NET_STAT_POLL = BASE + 40;
public static final int EVENT_DATA_RAT_CHANGED = BASE + 41;
public static final int CMD_CLEAR_PROVISIONING_SPINNER = BASE + 42;
+ public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 43;
/***** Constants *****/
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 2727319..b41d361 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -360,9 +360,9 @@
int getCallState();
/**
- * Returns the call state for a subId.
+ * Returns the call state for a slot.
*/
- int getCallStateForSubscriber(int subId);
+ int getCallStateForSlot(int slotId);
int getDataActivity();
int getDataState();
@@ -375,12 +375,12 @@
int getActivePhoneType();
/**
- * Returns the current active phone type as integer for particular subId.
+ * Returns the current active phone type as integer for particular slot.
* Returns TelephonyManager.PHONE_TYPE_CDMA if RILConstants.CDMA_PHONE
* and TelephonyManager.PHONE_TYPE_GSM if RILConstants.GSM_PHONE
- * @param subId user preferred subId.
+ * @param slotId - slot to query.
*/
- int getActivePhoneTypeForSubscriber(int subId);
+ int getActivePhoneTypeForSlot(int slotId);
/**
* Returns the CDMA ERI icon index to display
@@ -992,6 +992,26 @@
String getDeviceId(String callingPackage);
/**
+ * Returns the IMEI for the given slot.
+ *
+ * @param slotId - device slot.
+ * @param callingPackage The package making the call.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ */
+ String getImeiForSlot(int slotId, String callingPackage);
+
+ /**
+ * Returns the device software version.
+ *
+ * @param slotId - device slot.
+ * @param callingPackage The package making the call.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ */
+ String getDeviceSoftwareVersionForSlot(int slotId, String callingPackage);
+
+ /**
* Returns the subscription ID associated with the specified PhoneAccount.
*/
int getSubIdForPhoneAccount(in PhoneAccount phoneAccount);
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 91e891f..2e5ed3f 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -691,6 +691,15 @@
throw new UnsupportedOperationException();
}
+ /**
+ * @hide - to match hiding in superclass
+ */
+ @Override
+ public void deleteApplicationCacheFilesAsUser(String packageName, int userId,
+ IPackageDataObserver observer) {
+ throw new UnsupportedOperationException();
+ }
+
/** {@hide} */
@Override
public void freeStorageAndNotify(String volumeUuid, long idealStorageSize,
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 18fd985..b9e9ac8 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -374,6 +374,15 @@
</activity>
<activity
+ android:name="GetBitmapSurfaceViewActivity"
+ android:label="SurfaceView/GetBitmap with Camera source">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.hwui.TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name="GLTextureViewActivity"
android:label="TextureView/OpenGL">
<intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java
new file mode 100644
index 0000000..d3cd7db
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.PixelCopy;
+import android.graphics.PixelCopy.OnPixelCopyFinished;
+import android.graphics.PixelCopy.Response;
+import android.hardware.Camera;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.Gravity;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.Toast;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class GetBitmapSurfaceViewActivity extends Activity implements SurfaceHolder.Callback {
+ private Camera mCamera;
+ private SurfaceView mSurfaceView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FrameLayout content = new FrameLayout(this);
+
+ mSurfaceView = new SurfaceView(this);
+ mSurfaceView.getHolder().addCallback(this);
+
+ Button button = new Button(this);
+ button.setText("Copy bitmap to /sdcard/surfaceview.png");
+ button.setOnClickListener((View v) -> {
+ Bitmap b = Bitmap.createBitmap(
+ mSurfaceView.getWidth(),
+ mSurfaceView.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ PixelCopy.request(mSurfaceView, b,
+ mOnCopyFinished, mSurfaceView.getHandler());
+ });
+
+ content.addView(mSurfaceView, new FrameLayout.LayoutParams(500, 400, Gravity.CENTER));
+ content.addView(button, new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM));
+ setContentView(content);
+ }
+
+ private final OnPixelCopyFinished mOnCopyFinished = new OnPixelCopyFinished() {
+ @Override
+ public void onPixelCopyFinished(Response response) {
+ if (!response.success) {
+ Toast.makeText(GetBitmapSurfaceViewActivity.this,
+ "Failed to copy", Toast.LENGTH_SHORT).show();
+ return;
+ }
+ try {
+ try (FileOutputStream out = new FileOutputStream(
+ Environment.getExternalStorageDirectory() + "/surfaceview.png");) {
+ response.bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+ }
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ };
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mCamera = Camera.open();
+
+ try {
+ mCamera.setPreviewSurface(holder.getSurface());
+ } catch (IOException t) {
+ android.util.Log.e("TextureView", "Cannot set preview texture target!", t);
+ }
+
+ mCamera.startPreview();
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mCamera.stopPreview();
+ mCamera.release();
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java
index b1431c5..5c30fab 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java
@@ -17,18 +17,29 @@
package com.android.test.hwui;
import android.app.Activity;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.PixelCopy;
+import android.graphics.PixelCopy.OnPixelCopyFinished;
+import android.graphics.PixelCopy.Response;
import android.graphics.PorterDuff;
import android.os.Bundle;
-import android.view.Gravity;
+import android.os.Environment;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
+import android.view.View;
+import android.widget.Button;
import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.Toast;
-@SuppressWarnings({"UnusedDeclaration"})
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
public class HardwareCanvasSurfaceViewActivity extends Activity implements Callback {
private SurfaceView mSurfaceView;
private HardwareCanvasSurfaceViewActivity.RenderingThread mThread;
@@ -42,13 +53,49 @@
mSurfaceView = new SurfaceView(this);
mSurfaceView.getHolder().addCallback(this);
- content.addView(mSurfaceView, new FrameLayout.LayoutParams(
+ Button button = new Button(this);
+ button.setText("Copy bitmap to /sdcard/surfaceview.png");
+ button.setOnClickListener((View v) -> {
+ Bitmap b = Bitmap.createBitmap(
+ mSurfaceView.getWidth(),
+ mSurfaceView.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ PixelCopy.request(mSurfaceView, b,
+ mOnCopyFinished, mSurfaceView.getHandler());
+ });
+
+ LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.addView(button, LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ layout.addView(mSurfaceView, LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.MATCH_PARENT);
+
+ content.addView(layout, new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.MATCH_PARENT,
- Gravity.CENTER));
+ FrameLayout.LayoutParams.MATCH_PARENT));
setContentView(content);
}
+ private final OnPixelCopyFinished mOnCopyFinished = new OnPixelCopyFinished() {
+ @Override
+ public void onPixelCopyFinished(Response response) {
+ if (!response.success) {
+ Toast.makeText(HardwareCanvasSurfaceViewActivity.this,
+ "Failed to copy", Toast.LENGTH_SHORT).show();
+ return;
+ }
+ try {
+ try (FileOutputStream out = new FileOutputStream(
+ Environment.getExternalStorageDirectory() + "/surfaceview.png");) {
+ response.bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+ }
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ };
+
@Override
public void surfaceCreated(SurfaceHolder holder) {
mThread = new RenderingThread(holder.getSurface());
diff --git a/tests/SoundTriggerTestApp/res/layout/main.xml b/tests/SoundTriggerTestApp/res/layout/main.xml
index 702be49..06949a0 100644
--- a/tests/SoundTriggerTestApp/res/layout/main.xml
+++ b/tests/SoundTriggerTestApp/res/layout/main.xml
@@ -60,29 +60,22 @@
android:onClick="onUnEnrollButtonClicked"
android:padding="20dp" />
+ <Button
+ android:id="@+id/play_trigger_id"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/play_trigger"
+ android:onClick="onPlayTriggerButtonClicked"
+ android:padding="20dp" />
+
</LinearLayout>
<RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/model_group_id"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="20dp"
- android:checkedButton="@+id/model_one"
android:orientation="vertical">
- <RadioButton android:id="@+id/model_one"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/model_one"
- android:onClick="onRadioButtonClicked"/>
- <RadioButton android:id="@+id/model_two"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/model_two"
- android:onClick="onRadioButtonClicked"/>
- <RadioButton android:id="@+id/model_three"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/model_three"
- android:onClick="onRadioButtonClicked"/>
</RadioGroup>
<ScrollView
@@ -93,7 +86,7 @@
android:fillViewport="true">
<TextView
- android:id="@+id/console"
+ android:id="@+id/console"
android:paddingTop="20pt"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
diff --git a/tests/SoundTriggerTestApp/res/values/strings.xml b/tests/SoundTriggerTestApp/res/values/strings.xml
index b4ca71b..7c1f649 100644
--- a/tests/SoundTriggerTestApp/res/values/strings.xml
+++ b/tests/SoundTriggerTestApp/res/values/strings.xml
@@ -21,8 +21,6 @@
<string name="unenroll">Un-load</string>
<string name="start_recog">Start</string>
<string name="stop_recog">Stop</string>
- <string name="model_one">Model One</string>
- <string name="model_two">Model Two</string>
- <string name="model_three">Model Three</string>
+ <string name="play_trigger">Play Trigger Audio</string>
<string name="none">Debug messages appear here:\n</string>
</resources>
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java
index 3ca96d2..5fd38e9 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java
@@ -16,24 +16,36 @@
package com.android.test.soundtrigger;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
import java.util.Random;
import java.util.UUID;
import android.app.Activity;
-import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
+import android.hardware.soundtrigger.SoundTrigger;
import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
import android.media.soundtrigger.SoundTriggerDetector;
import android.media.soundtrigger.SoundTriggerManager;
-import android.text.Editable;
-import android.text.method.ScrollingMovementMethod;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.os.UserManager;
+import android.text.Editable;
+import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
+import android.widget.Button;
import android.widget.RadioButton;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
@@ -44,20 +56,17 @@
private SoundTriggerUtil mSoundTriggerUtil;
private Random mRandom;
- private UUID mModelUuid1 = UUID.randomUUID();
- private UUID mModelUuid2 = UUID.randomUUID();
- private UUID mModelUuid3 = UUID.randomUUID();
- private UUID mVendorUuid = UUID.randomUUID();
- private SoundTriggerDetector mDetector1 = null;
- private SoundTriggerDetector mDetector2 = null;
- private SoundTriggerDetector mDetector3 = null;
+ private Map<Integer, ModelInfo> mModelInfoMap;
+ private Map<View, Integer> mModelIdMap;
private TextView mDebugView = null;
- private int mSelectedModelId = 1;
+ private int mSelectedModelId = -1;
private ScrollView mScrollView = null;
+ private Button mPlayTriggerButton = null;
private PowerManager.WakeLock mScreenWakelock;
private Handler mHandler;
+ private RadioGroup mRadioGroup;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -66,11 +75,108 @@
setContentView(R.layout.main);
mDebugView = (TextView) findViewById(R.id.console);
mScrollView = (ScrollView) findViewById(R.id.scroller_id);
+ mRadioGroup = (RadioGroup) findViewById(R.id.model_group_id);
+ mPlayTriggerButton = (Button) findViewById(R.id.play_trigger_id);
mDebugView.setText(mDebugView.getText(), TextView.BufferType.EDITABLE);
mDebugView.setMovementMethod(new ScrollingMovementMethod());
mSoundTriggerUtil = new SoundTriggerUtil(this);
mRandom = new Random();
mHandler = new Handler();
+
+ mModelInfoMap = new HashMap();
+ mModelIdMap = new HashMap();
+
+ setVolumeControlStream(AudioManager.STREAM_MUSIC);
+
+ // Load all the models in the data dir.
+ for (File file : getFilesDir().listFiles()) {
+ // Find meta-data in .properties files, ignore everything else.
+ if (!file.getName().endsWith(".properties")) {
+ continue;
+ }
+ try {
+ Properties properties = new Properties();
+ properties.load(new FileInputStream(file));
+ createModelInfoAndWidget(properties);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to load properties file " + file.getName());
+ }
+ }
+
+ // Create a few dummy models if we didn't load anything.
+ if (mModelIdMap.isEmpty()) {
+ Properties dummyModelProperties = new Properties();
+ for (String name : new String[]{"One", "Two", "Three"}) {
+ dummyModelProperties.setProperty("name", "Model " + name);
+ createModelInfoAndWidget(dummyModelProperties);
+ }
+ }
+ }
+
+ private void createModelInfoAndWidget(Properties properties) {
+ try {
+ ModelInfo modelInfo = new ModelInfo();
+
+ if (!properties.containsKey("name")) {
+ throw new RuntimeException("must have a 'name' property");
+ }
+ modelInfo.name = properties.getProperty("name");
+
+ if (properties.containsKey("modelUuid")) {
+ modelInfo.modelUuid = UUID.fromString(properties.getProperty("modelUuid"));
+ } else {
+ modelInfo.modelUuid = UUID.randomUUID();
+ }
+
+ if (properties.containsKey("vendorUuid")) {
+ modelInfo.vendorUuid = UUID.fromString(properties.getProperty("vendorUuid"));
+ } else {
+ modelInfo.vendorUuid = UUID.randomUUID();
+ }
+
+ if (properties.containsKey("triggerAudio")) {
+ modelInfo.triggerAudioPlayer = MediaPlayer.create(this, Uri.parse(
+ getFilesDir().getPath() + "/" + properties.getProperty("triggerAudio")));
+ }
+
+ if (properties.containsKey("dataFile")) {
+ File modelDataFile = new File(
+ getFilesDir().getPath() + "/" + properties.getProperty("dataFile"));
+ modelInfo.modelData = new byte[(int) modelDataFile.length()];
+ FileInputStream input = new FileInputStream(modelDataFile);
+ input.read(modelInfo.modelData, 0, modelInfo.modelData.length);
+ } else {
+ modelInfo.modelData = new byte[1024];
+ mRandom.nextBytes(modelInfo.modelData);
+ }
+
+ // TODO: Add property support for keyphrase models when they're exposed by the
+ // service. Also things like how much audio they should record with the capture session
+ // provided in the callback.
+
+ // Add a widget into the radio group.
+ RadioButton button = new RadioButton(this);
+ mRadioGroup.addView(button);
+ button.setText(modelInfo.name);
+ button.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ onRadioButtonClicked(v);
+ }
+ });
+
+ // Update our maps containing the button -> id and id -> modelInfo.
+ int newModelId = mModelIdMap.size() + 1;
+ mModelIdMap.put(button, newModelId);
+ mModelInfoMap.put(newModelId, modelInfo);
+
+ // If we don't have something selected, select this first thing.
+ if (mSelectedModelId < 0) {
+ button.setChecked(true);
+ onRadioButtonClicked(button);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error parsing properties for " + properties.getProperty("name"), e);
+ }
}
private void postMessage(String msg) {
@@ -91,27 +197,15 @@
}
private synchronized UUID getSelectedUuid() {
- if (mSelectedModelId == 2) return mModelUuid2;
- if (mSelectedModelId == 3) return mModelUuid3;
- return mModelUuid1; // Default.
+ return mModelInfoMap.get(mSelectedModelId).modelUuid;
}
private synchronized void setDetector(SoundTriggerDetector detector) {
- if (mSelectedModelId == 2) {
- mDetector2 = detector;
- return;
- }
- if (mSelectedModelId == 3) {
- mDetector3 = detector;
- return;
- }
- mDetector1 = detector;
+ mModelInfoMap.get(mSelectedModelId).detector = detector;
}
private synchronized SoundTriggerDetector getDetector() {
- if (mSelectedModelId == 2) return mDetector2;
- if (mSelectedModelId == 3) return mDetector3;
- return mDetector1;
+ return mModelInfoMap.get(mSelectedModelId).detector;
}
private void screenWakeup() {
@@ -127,25 +221,29 @@
mScreenWakelock.release();
}
+ /** TODO: Should return the abstract sound model that can be then sent to the service. */
+ private GenericSoundModel createNewSoundModel() {
+ ModelInfo modelInfo = mModelInfoMap.get(mSelectedModelId);
+ return new GenericSoundModel(modelInfo.modelUuid, modelInfo.vendorUuid,
+ modelInfo.modelData);
+ }
+
/**
* Called when the user clicks the enroll button.
* Performs a fresh enrollment.
*/
public void onEnrollButtonClicked(View v) {
postMessage("Loading model: " + mSelectedModelId);
- // Generate a fake model to push.
- byte[] data = new byte[1024];
- mRandom.nextBytes(data);
- UUID modelUuid = getSelectedUuid();
- GenericSoundModel model = new GenericSoundModel(modelUuid, mVendorUuid, data);
+
+ GenericSoundModel model = createNewSoundModel();
boolean status = mSoundTriggerUtil.addOrUpdateSoundModel(model);
if (status) {
Toast.makeText(
- this, "Successfully created sound trigger model UUID=" + modelUuid,
+ this, "Successfully created sound trigger model UUID=" + model.uuid,
Toast.LENGTH_SHORT).show();
} else {
- Toast.makeText(this, "Failed to enroll!!!" + modelUuid, Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, "Failed to enroll!!!" + model.uuid, Toast.LENGTH_SHORT).show();
}
// Test the SoundManager API.
@@ -185,11 +283,7 @@
Toast.makeText(this, "Sound model not found!!!", Toast.LENGTH_SHORT).show();
return;
}
- // Generate a fake model to push.
- byte[] data = new byte[2048];
- mRandom.nextBytes(data);
- GenericSoundModel updated = new GenericSoundModel(soundModel.uuid,
- soundModel.vendorUuid, data);
+ GenericSoundModel updated = createNewSoundModel();
boolean status = mSoundTriggerUtil.addOrUpdateSoundModel(updated);
if (status) {
Toast.makeText(this, "Successfully re-enrolled, model UUID=" + updated.uuid,
@@ -237,29 +331,32 @@
public synchronized void onRadioButtonClicked(View view) {
// Is the button now checked?
boolean checked = ((RadioButton) view).isChecked();
- // Check which radio button was clicked
- switch(view.getId()) {
- case R.id.model_one:
- if (checked) {
- mSelectedModelId = 1;
- postMessage("Selected model one.");
- }
- break;
- case R.id.model_two:
- if (checked) {
- mSelectedModelId = 2;
- postMessage("Selected model two.");
- }
- break;
- case R.id.model_three:
- if (checked) {
- mSelectedModelId = 3;
- postMessage("Selected model three.");
- }
- break;
+ if (checked) {
+ mSelectedModelId = mModelIdMap.get(view);
+ ModelInfo modelInfo = mModelInfoMap.get(mSelectedModelId);
+ postMessage("Selected " + modelInfo.name);
+
+ // Set the play trigger button to be enabled only if we actually have some audio.
+ mPlayTriggerButton.setEnabled(modelInfo.triggerAudioPlayer != null);
}
}
+ public synchronized void onPlayTriggerButtonClicked(View v) {
+ ModelInfo modelInfo = mModelInfoMap.get(mSelectedModelId);
+ modelInfo.triggerAudioPlayer.start();
+ postMessage("Playing trigger audio for " + modelInfo.name);
+ }
+
+ // Helper struct for holding information about a model.
+ private static class ModelInfo {
+ public String name;
+ public UUID modelUuid;
+ public UUID vendorUuid;
+ public MediaPlayer triggerAudioPlayer;
+ public SoundTriggerDetector detector;
+ public byte modelData[];
+ };
+
// Implementation of SoundTriggerDetector.Callback.
public class DetectorCallback extends SoundTriggerDetector.Callback {
public void onAvailabilityChanged(int status) {
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
index b5ed1b5..81ab3cc 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_lint.py
@@ -1,13 +1,17 @@
#!/usr/bin/env python
import collections
+import copy
import glob
+import itertools
from os import path
import sys
from xml.etree import ElementTree
from fontTools import ttLib
+EMOJI_VS = 0xFE0F
+
LANG_TO_SCRIPT = {
'as': 'Beng',
'bn': 'Beng',
@@ -57,13 +61,26 @@
return LANG_TO_SCRIPT[lang]
-def get_best_cmap(font):
+def printable(inp):
+ if type(inp) is set: # set of character sequences
+ return '{' + ', '.join([printable(seq) for seq in inp]) + '}'
+ if type(inp) is tuple: # character sequence
+ return '<' + (', '.join([printable(ch) for ch in inp])) + '>'
+ else: # single character
+ return 'U+%04X' % inp
+
+
+def open_font(font):
font_file, index = font
font_path = path.join(_fonts_dir, font_file)
if index is not None:
- ttfont = ttLib.TTFont(font_path, fontNumber=index)
+ return ttLib.TTFont(font_path, fontNumber=index)
else:
- ttfont = ttLib.TTFont(font_path)
+ return ttLib.TTFont(font_path)
+
+
+def get_best_cmap(font):
+ ttfont = open_font(font)
all_unicode_cmap = None
bmp_cmap = None
for cmap in ttfont['cmap'].tables:
@@ -79,6 +96,52 @@
return all_unicode_cmap.cmap if all_unicode_cmap else bmp_cmap.cmap
+def get_variation_sequences_cmap(font):
+ ttfont = open_font(font)
+ vs_cmap = None
+ for cmap in ttfont['cmap'].tables:
+ specifier = (cmap.format, cmap.platformID, cmap.platEncID)
+ if specifier == (14, 0, 5):
+ assert vs_cmap is None, 'More than one VS cmap in %s' % (font, )
+ vs_cmap = cmap
+ return vs_cmap
+
+
+def get_emoji_map(font):
+ # Add normal characters
+ emoji_map = copy.copy(get_best_cmap(font))
+ reverse_cmap = {glyph: code for code, glyph in emoji_map.items()}
+
+ # Add variation sequences
+ vs_dict = get_variation_sequences_cmap(font).uvsDict
+ for vs in vs_dict:
+ for base, glyph in vs_dict[vs]:
+ if glyph is None:
+ emoji_map[(base, vs)] = emoji_map[base]
+ else:
+ emoji_map[(base, vs)] = glyph
+
+ # Add GSUB rules
+ ttfont = open_font(font)
+ for lookup in ttfont['GSUB'].table.LookupList.Lookup:
+ assert lookup.LookupType == 4, 'We only understand type 4 lookups'
+ for subtable in lookup.SubTable:
+ ligatures = subtable.ligatures
+ for first_glyph in ligatures:
+ for ligature in ligatures[first_glyph]:
+ sequence = [first_glyph] + ligature.Component
+ sequence = [reverse_cmap[glyph] for glyph in sequence]
+ sequence = tuple(sequence)
+ # Make sure no starting subsequence of 'sequence' has been
+ # seen before.
+ for sub_len in range(2, len(sequence)+1):
+ subsequence = sequence[:sub_len]
+ assert subsequence not in emoji_map
+ emoji_map[sequence] = ligature.LigGlyph
+
+ return emoji_map
+
+
def assert_font_supports_any_of_chars(font, chars):
best_cmap = get_best_cmap(font)
for char in chars:
@@ -101,6 +164,13 @@
'U+%04X was found in %s' % (char, font))
+def assert_font_supports_all_sequences(font, sequences):
+ vs_dict = get_variation_sequences_cmap(font).uvsDict
+ for base, vs in sorted(sequences):
+ assert vs in vs_dict and (base, None) in vs_dict[vs], (
+ '<U+%04X, U+%04X> was not found in %s' % (base, vs, font))
+
+
def check_hyphens(hyphens_dir):
# Find all the scripts that need automatic hyphenation
scripts = set()
@@ -119,6 +189,16 @@
assert_font_supports_any_of_chars(font, HYPHENS)
+class FontRecord(object):
+ def __init__(self, name, scripts, variant, weight, style, font):
+ self.name = name
+ self.scripts = scripts
+ self.variant = variant
+ self.weight = weight
+ self.style = style
+ self.font = font
+
+
def parse_fonts_xml(fonts_xml_path):
global _script_to_font_map, _fallback_chain
_script_to_font_map = collections.defaultdict(set)
@@ -159,7 +239,7 @@
if index:
index = int(index)
- _fallback_chain.append((
+ _fallback_chain.append(FontRecord(
name,
frozenset(scripts),
variant,
@@ -175,39 +255,72 @@
_script_to_font_map[script].add((font_file, index))
-def check_emoji_availability():
- emoji_fonts = [font[5] for font in _fallback_chain if 'Zsye' in font[1]]
+def check_emoji_coverage(all_emoji, equivalent_emoji):
+ emoji_fonts = [
+ record.font for record in _fallback_chain
+ if 'Zsye' in record.scripts]
assert len(emoji_fonts) == 1, 'There are %d emoji fonts.' % len(emoji_fonts)
emoji_font = emoji_fonts[0]
- emoji_chars = _emoji_properties['Emoji']
- assert_font_supports_all_of_chars(emoji_font, emoji_chars)
+ coverage = get_emoji_map(emoji_font)
+
+ for sequence in all_emoji:
+ assert sequence in coverage, (
+ '%s is not supported in the emoji font.' % printable(sequence))
+
+ for sequence in coverage:
+ if sequence in {0x0000, 0x000D, 0x0020}:
+ # The font needs to support a few extra characters, which is OK
+ continue
+ assert sequence in all_emoji, (
+ 'Emoji font should not support %s.' % printable(sequence))
+
+ for first, second in sorted(equivalent_emoji.items()):
+ assert coverage[first] == coverage[second], (
+ '%s and %s should map to the same glyph.' % (
+ printable(first),
+ printable(second)))
+
+ for glyph in set(coverage.values()):
+ maps_to_glyph = [seq for seq in coverage if coverage[seq] == glyph]
+ if len(maps_to_glyph) > 1:
+ # There are more than one sequences mapping to the same glyph. We
+ # need to make sure they were expected to be equivalent.
+ equivalent_seqs = set()
+ for seq in maps_to_glyph:
+ equivalent_seq = seq
+ while equivalent_seq in equivalent_emoji:
+ equivalent_seq = equivalent_emoji[equivalent_seq]
+ equivalent_seqs.add(equivalent_seq)
+ assert len(equivalent_seqs) == 1, (
+ 'The sequences %s should not result in the same glyph %s' % (
+ printable(equivalent_seqs),
+ glyph))
-def check_emoji_defaults():
- default_emoji_chars = _emoji_properties['Emoji_Presentation']
- missing_text_chars = _emoji_properties['Emoji'] - default_emoji_chars
+def check_emoji_defaults(default_emoji):
+ missing_text_chars = _emoji_properties['Emoji'] - default_emoji
emoji_font_seen = False
- for name, scripts, variant, weight, style, font in _fallback_chain:
- if 'Zsye' in scripts:
+ for record in _fallback_chain:
+ if 'Zsye' in record.scripts:
emoji_font_seen = True
# No need to check the emoji font
continue
# For later fonts, we only check them if they have a script
# defined, since the defined script may get them to a higher
# score even if they appear after the emoji font.
- if emoji_font_seen and not scripts:
+ if emoji_font_seen and not record.scripts:
continue
# Check default emoji-style characters
- assert_font_supports_none_of_chars(font, sorted(default_emoji_chars))
+ assert_font_supports_none_of_chars(record.font, sorted(default_emoji))
# Mark default text-style characters appearing in fonts above the emoji
# font as seen
if not emoji_font_seen:
- missing_text_chars -= set(get_best_cmap(font))
+ missing_text_chars -= set(get_best_cmap(record.font))
- # Noto does not have monochrome symbols for Unicode 7.0 wingdings and
- # webdings
+ # Noto does not have monochrome glyphs for Unicode 7.0 wingdings and
+ # webdings yet.
missing_text_chars -= _chars_by_age['7.0']
# TODO: Remove these after b/26113320 is fixed
missing_text_chars -= {
@@ -236,31 +349,175 @@
line = line.strip()
if not line:
continue
- char_range, prop = line.split(';')
- char_range = char_range.strip()
+
+ chars, prop = line.split(';')
+ chars = chars.strip()
prop = prop.strip()
- if '..' in char_range:
- char_start, char_end = char_range.split('..')
- else:
- char_start = char_end = char_range
- char_start = int(char_start, 16)
- char_end = int(char_end, 16)
- char_range = xrange(char_start, char_end+1)
+
+ if ' ' in chars: # character sequence
+ sequence = [int(ch, 16) for ch in chars.split(' ')]
+ additions = [tuple(sequence)]
+ elif '..' in chars: # character range
+ char_start, char_end = chars.split('..')
+ char_start = int(char_start, 16)
+ char_end = int(char_end, 16)
+ additions = xrange(char_start, char_end+1)
+ else: # singe character
+ additions = [int(chars, 16)]
if reverse:
- output_dict[prop].update(char_range)
+ output_dict[prop].update(additions)
else:
- for char in char_range:
- assert char not in output_dict
- output_dict[char] = prop
+ for addition in additions:
+ assert addition not in output_dict
+ output_dict[addition] = prop
return output_dict
+def parse_standardized_variants(file_path):
+ emoji_set = set()
+ text_set = set()
+ with open(file_path) as datafile:
+ for line in datafile:
+ if '#' in line:
+ line = line[:line.index('#')]
+ line = line.strip()
+ if not line:
+ continue
+ sequence, description, _ = line.split(';')
+ sequence = sequence.strip().split(' ')
+ base = int(sequence[0], 16)
+ vs = int(sequence[1], 16)
+ description = description.strip()
+ if description == 'text style':
+ text_set.add((base, vs))
+ elif description == 'emoji style':
+ emoji_set.add((base, vs))
+ return text_set, emoji_set
+
+
def parse_ucd(ucd_path):
global _emoji_properties, _chars_by_age
+ global _text_variation_sequences, _emoji_variation_sequences
+ global _emoji_sequences, _emoji_zwj_sequences
_emoji_properties = parse_unicode_datafile(
path.join(ucd_path, 'emoji-data.txt'), reverse=True)
_chars_by_age = parse_unicode_datafile(
path.join(ucd_path, 'DerivedAge.txt'), reverse=True)
+ sequences = parse_standardized_variants(
+ path.join(ucd_path, 'StandardizedVariants.txt'))
+ _text_variation_sequences, _emoji_variation_sequences = sequences
+ _emoji_sequences = parse_unicode_datafile(
+ path.join(ucd_path, 'emoji-sequences.txt'))
+ _emoji_zwj_sequences = parse_unicode_datafile(
+ path.join(ucd_path, 'emoji-zwj-sequences.txt'))
+
+
+def flag_sequence(territory_code):
+ return tuple(0x1F1E6 + ord(ch) - ord('A') for ch in territory_code)
+
+
+UNSUPPORTED_FLAGS = frozenset({
+ flag_sequence('BL'), flag_sequence('BQ'), flag_sequence('DG'),
+ flag_sequence('EA'), flag_sequence('EH'), flag_sequence('FK'),
+ flag_sequence('GF'), flag_sequence('GP'), flag_sequence('GS'),
+ flag_sequence('MF'), flag_sequence('MQ'), flag_sequence('NC'),
+ flag_sequence('PM'), flag_sequence('RE'), flag_sequence('TF'),
+ flag_sequence('WF'), flag_sequence('XK'), flag_sequence('YT'),
+})
+
+EQUIVALENT_FLAGS = {
+ flag_sequence('BV'): flag_sequence('NO'),
+ flag_sequence('CP'): flag_sequence('FR'),
+ flag_sequence('HM'): flag_sequence('AU'),
+ flag_sequence('SJ'): flag_sequence('NO'),
+ flag_sequence('UM'): flag_sequence('US'),
+}
+
+COMBINING_KEYCAP = 0x20E3
+
+LEGACY_ANDROID_EMOJI = {
+ 0xFE4E5: flag_sequence('JP'),
+ 0xFE4E6: flag_sequence('US'),
+ 0xFE4E7: flag_sequence('FR'),
+ 0xFE4E8: flag_sequence('DE'),
+ 0xFE4E9: flag_sequence('IT'),
+ 0xFE4EA: flag_sequence('GB'),
+ 0xFE4EB: flag_sequence('ES'),
+ 0xFE4EC: flag_sequence('RU'),
+ 0xFE4ED: flag_sequence('CN'),
+ 0xFE4EE: flag_sequence('KR'),
+ 0xFE82C: (ord('#'), COMBINING_KEYCAP),
+ 0xFE82E: (ord('1'), COMBINING_KEYCAP),
+ 0xFE82F: (ord('2'), COMBINING_KEYCAP),
+ 0xFE830: (ord('3'), COMBINING_KEYCAP),
+ 0xFE831: (ord('4'), COMBINING_KEYCAP),
+ 0xFE832: (ord('5'), COMBINING_KEYCAP),
+ 0xFE833: (ord('6'), COMBINING_KEYCAP),
+ 0xFE834: (ord('7'), COMBINING_KEYCAP),
+ 0xFE835: (ord('8'), COMBINING_KEYCAP),
+ 0xFE836: (ord('9'), COMBINING_KEYCAP),
+ 0xFE837: (ord('0'), COMBINING_KEYCAP),
+}
+
+ZWJ_IDENTICALS = {
+ # KISS
+ (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468): 0x1F48F,
+ # COUPLE WITH HEART
+ (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F468): 0x1F491,
+ # FAMILY
+ (0x1F468, 0x200D, 0x1F469, 0x200D, 0x1F466): 0x1F46A,
+}
+
+def compute_expected_emoji():
+ equivalent_emoji = {}
+ sequence_pieces = set()
+ all_sequences = set()
+ all_sequences.update(_emoji_variation_sequences)
+
+ for sequence in _emoji_sequences.keys():
+ sequence = tuple(ch for ch in sequence if ch != EMOJI_VS)
+ all_sequences.add(sequence)
+ sequence_pieces.update(sequence)
+
+ for sequence in _emoji_zwj_sequences.keys():
+ sequence = tuple(ch for ch in sequence if ch != EMOJI_VS)
+ all_sequences.add(sequence)
+ sequence_pieces.update(sequence)
+ # Add reverse of all emoji ZWJ sequences, which are added to the fonts
+ # as a workaround to get the sequences work in RTL text.
+ reversed_seq = tuple(reversed(sequence))
+ all_sequences.add(reversed_seq)
+ equivalent_emoji[reversed_seq] = sequence
+
+ # Add all two-letter flag sequences, as even the unsupported ones should
+ # resolve to a flag tofu.
+ all_letters = [chr(code) for code in range(ord('A'), ord('Z')+1)]
+ all_two_letter_codes = itertools.product(all_letters, repeat=2)
+ all_flags = {flag_sequence(code) for code in all_two_letter_codes}
+ all_sequences.update(all_flags)
+ tofu_flags = UNSUPPORTED_FLAGS | (all_flags - set(_emoji_sequences.keys()))
+
+ all_emoji = (
+ _emoji_properties['Emoji'] |
+ all_sequences |
+ sequence_pieces |
+ set(LEGACY_ANDROID_EMOJI.keys()))
+ default_emoji = (
+ _emoji_properties['Emoji_Presentation'] |
+ all_sequences |
+ set(LEGACY_ANDROID_EMOJI.keys()))
+
+ first_tofu_flag = sorted(tofu_flags)[0]
+ for flag in tofu_flags:
+ if flag != first_tofu_flag:
+ equivalent_emoji[flag] = first_tofu_flag
+ equivalent_emoji.update(EQUIVALENT_FLAGS)
+ equivalent_emoji.update(LEGACY_ANDROID_EMOJI)
+ equivalent_emoji.update(ZWJ_IDENTICALS)
+ for seq in _emoji_variation_sequences:
+ equivalent_emoji[seq] = seq[0]
+
+ return all_emoji, default_emoji, equivalent_emoji
def main():
@@ -278,8 +535,9 @@
if check_emoji == 'true':
ucd_path = sys.argv[3]
parse_ucd(ucd_path)
- check_emoji_availability()
- check_emoji_defaults()
+ all_emoji, default_emoji, equivalent_emoji = compute_expected_emoji()
+ check_emoji_coverage(all_emoji, equivalent_emoji)
+ check_emoji_defaults(default_emoji)
if __name__ == '__main__':
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
index 7412bc2..50efc7f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -245,6 +245,13 @@
return sFontLocation;
}
+ // ---- delegate methods ----
+ @LayoutlibDelegate
+ /*package*/ static boolean addFont(FontFamily thisFontFamily, String path, int ttcIndex) {
+ final FontFamily_Delegate delegate = getDelegate(thisFontFamily.mNativePtr);
+ return delegate != null && delegate.addFont(path, ttcIndex);
+ }
+
// ---- native methods ----
@LayoutlibDelegate
@@ -270,16 +277,8 @@
}
@LayoutlibDelegate
- /*package*/ static boolean nAddFont(long nativeFamily, final String path, int ttcIndex) {
- // FIXME: support ttc fonts. Hack JRE??
- final FontFamily_Delegate delegate = getDelegate(nativeFamily);
- if (delegate != null) {
- if (sFontLocation == null) {
- delegate.mPostInitRunnables.add(() -> delegate.addFont(path));
- return true;
- }
- return delegate.addFont(path);
- }
+ /*package*/ static boolean nAddFont(long nativeFamily, ByteBuffer font, int ttcIndex) {
+ assert false : "The only client of this method has been overriden.";
return false;
}
@@ -390,6 +389,15 @@
mPostInitRunnables = null;
}
+ private boolean addFont(final String path, int ttcIndex) {
+ // FIXME: support ttc fonts. Hack JRE??
+ if (sFontLocation == null) {
+ mPostInitRunnables.add(() -> addFont(path));
+ return true;
+ }
+ return addFont(path);
+ }
+
private boolean addFont(@NonNull String path) {
return addFont(path, DEFAULT_FONT_WEIGHT, path.endsWith(FONT_SUFFIX_ITALIC));
}
diff --git a/tools/layoutlib/bridge/src/android/util/PathParser_Delegate.java b/tools/layoutlib/bridge/src/android/util/PathParser_Delegate.java
index 6c34c70..6d3bb4c 100644
--- a/tools/layoutlib/bridge/src/android/util/PathParser_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/util/PathParser_Delegate.java
@@ -64,15 +64,14 @@
}
@LayoutlibDelegate
- /*package*/ static boolean nParseStringForPath(long pathPtr, @NonNull String pathString, int
+ /*package*/ static void nParseStringForPath(long pathPtr, @NonNull String pathString, int
stringLength) {
Path_Delegate path_delegate = Path_Delegate.getDelegate(pathPtr);
if (path_delegate == null) {
- return false;
+ return;
}
assert pathString.length() == stringLength;
PathDataNode.nodesToPath(createNodesFromPathData(pathString), path_delegate);
- return true;
}
@LayoutlibDelegate
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index fea633e..308488a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -70,23 +70,6 @@
}
@Override
- public Map<String, String> getDefaultProperties(Object viewObject) {
- return mSession.getDefaultProperties(viewObject);
- }
-
- @Override
- public Result getProperty(Object objectView, String propertyName) {
- // pass
- return super.getProperty(objectView, propertyName);
- }
-
- @Override
- public Result setProperty(Object objectView, String propertyName, String propertyValue) {
- // pass
- return super.setProperty(objectView, propertyName, propertyValue);
- }
-
- @Override
public Result render(long timeout, boolean forceMeasure) {
try {
Bridge.prepareThread();
@@ -213,6 +196,10 @@
}
}
+ public RenderSessionImpl getSessionImpl() {
+ return mSession;
+ }
+
/*package*/ BridgeRenderSession(RenderSessionImpl scene, Result lastResult) {
mSession = scene;
if (scene != null) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 89272fa..fd95bd5 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -27,6 +27,7 @@
import com.android.ide.common.rendering.api.StyleResourceValue;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.layoutlib.bridge.android.PropertiesMap.Property;
import com.android.layoutlib.bridge.android.view.WindowManagerImpl;
import com.android.layoutlib.bridge.impl.ParserFactory;
import com.android.layoutlib.bridge.impl.Stack;
@@ -275,7 +276,7 @@
return mRenderResources;
}
- public Map<String, String> getDefaultPropMap(Object key) {
+ public PropertiesMap getDefaultPropMap(Object key) {
return mDefaultPropMaps.get(key);
}
@@ -731,16 +732,10 @@
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
"Failed to find the style corresponding to the id " + defStyleAttr, null);
} else {
- if (defaultPropMap != null) {
- String defStyleName = defStyleAttribute.getFirst();
- if (defStyleAttribute.getSecond()) {
- defStyleName = "android:" + defStyleName;
- }
- defaultPropMap.put("style", defStyleName);
- }
+ String defStyleName = defStyleAttribute.getFirst();
// look for the style in the current theme, and its parent:
- ResourceValue item = mRenderResources.findItemInTheme(defStyleAttribute.getFirst(),
+ ResourceValue item = mRenderResources.findItemInTheme(defStyleName,
defStyleAttribute.getSecond());
if (item != null) {
@@ -750,6 +745,12 @@
if (item instanceof StyleResourceValue) {
defStyleValues = (StyleResourceValue) item;
}
+ if (defaultPropMap != null) {
+ if (defStyleAttribute.getSecond()) {
+ defStyleName = "android:" + defStyleName;
+ }
+ defaultPropMap.put("style", new Property(defStyleName, item.getValue()));
+ }
} else {
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR,
String.format(
@@ -776,7 +777,8 @@
item = mRenderResources.getStyle(value.getSecond(), isFrameworkRes);
if (item != null) {
if (defaultPropMap != null) {
- defaultPropMap.put("style", item.getName());
+ String name = item.getName();
+ defaultPropMap.put("style", new Property(name, name));
}
defStyleValues = item;
@@ -855,13 +857,14 @@
// if we found a value, we make sure this doesn't reference another value.
// So we resolve it.
if (resValue != null) {
- // put the first default value, before the resolution.
- if (defaultPropMap != null) {
- defaultPropMap.put(attrName, resValue.getValue());
- }
-
+ String preResolve = resValue.getValue();
resValue = mRenderResources.resolveResValue(resValue);
+ if (defaultPropMap != null) {
+ defaultPropMap.put(attrName,
+ new Property(preResolve, resValue.getValue()));
+ }
+
// If the value is a reference to another theme attribute that doesn't
// exist, we should log a warning and omit it.
String val = resValue.getValue();
@@ -949,10 +952,11 @@
if (resValue != null) {
// Add it to defaultPropMap before resolving
- defaultPropMap.put(attrName, resValue.getValue());
+ String preResolve = resValue.getValue();
// resolve it to make sure there are no references left.
- ta.bridgeSetValue(i, attrName, attribute.getSecond(),
- mRenderResources.resolveResValue(resValue));
+ resValue = mRenderResources.resolveResValue(resValue);
+ ta.bridgeSetValue(i, attrName, attribute.getSecond(), resValue);
+ defaultPropMap.put(attrName, new Property(preResolve, resValue.getValue()));
}
}
}
@@ -1915,11 +1919,4 @@
}
}
-
- /**
- * An alias used for the value in {@code {@link #mDefaultPropMaps}}
- */
- private static class PropertiesMap extends HashMap<String, String> {
- }
-
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index 42c0ae0..0a64b63 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -645,6 +645,11 @@
}
@Override
+ public void deleteApplicationCacheFilesAsUser(String packageName, int userId,
+ IPackageDataObserver observer) {
+ }
+
+ @Override
public void freeStorageAndNotify(String volumeUuid, long freeStorageSize,
IPackageDataObserver observer) {
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/PropertiesMap.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/PropertiesMap.java
new file mode 100644
index 0000000..a38d579
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/PropertiesMap.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.android;
+
+import com.android.layoutlib.bridge.android.PropertiesMap.Property;
+
+import java.util.HashMap;
+
+/**
+ * An alias used for the value in {@link BridgeContext#mDefaultPropMaps}
+ */
+public class PropertiesMap extends HashMap<String, Property> {
+
+ public static class Property {
+ public final String resource;
+ public final String value;
+
+ public Property(String resource, String value) {
+ this.resource = resource;
+ this.value = value;
+ }
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 0c53753..2d38831 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -286,7 +286,7 @@
return mParams;
}
- protected BridgeContext getContext() {
+ public BridgeContext getContext() {
return mContext;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 866b248..11fabc6 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -1415,10 +1415,6 @@
return mSystemViewInfoList;
}
- public Map<String, String> getDefaultProperties(Object viewObject) {
- return getContext().getDefaultPropMap(viewObject);
- }
-
public void setScene(RenderSession session) {
mScene = session;
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 483bddc..061bed7 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -192,6 +192,7 @@
"android.graphics.BitmapFactory#setDensityFromOptions",
"android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#useLastSeenTarget",
"android.graphics.drawable.GradientDrawable#buildRing",
+ "android.graphics.FontFamily#addFont",
"android.graphics.Typeface#getSystemFontConfigLocation",
"android.graphics.Typeface#makeFamilyFromParsed",
"android.os.Handler#sendMessageAtTime",
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 0f84506..be5b8fc 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -149,7 +149,7 @@
void setAllowScansWithTraffic(int enabled);
int getAllowScansWithTraffic();
- boolean enableAutoJoinWhenAssociated(boolean enabled);
+ boolean setEnableAutoJoinWhenAssociated(boolean enabled);
boolean getEnableAutoJoinWhenAssociated();
void enableWifiConnectivityManager(boolean enabled);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 6984fe2..1cd32b6 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2700,9 +2700,9 @@
* @return true -- if set successful false -- if set failed
* @hide
*/
- public boolean enableAutoJoinWhenAssociated(boolean enabled) {
+ public boolean setEnableAutoJoinWhenAssociated(boolean enabled) {
try {
- return mService.enableAutoJoinWhenAssociated(enabled);
+ return mService.setEnableAutoJoinWhenAssociated(enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}