Merge "Add functions for setting/getting phone accounts." into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index 6fbd40b..22755b6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -905,6 +905,8 @@
field public static final int multiprocess = 16842771; // 0x1010013
field public static final int name = 16842755; // 0x1010003
field public static final int navigationBarColor = 16843858; // 0x1010452
+ field public static final int navigationContentDescription = 16843970; // 0x10104c2
+ field public static final int navigationIcon = 16843969; // 0x10104c1
field public static final int navigationMode = 16843471; // 0x10102cf
field public static final int negativeButtonText = 16843254; // 0x10101f6
field public static final int nestedScrollingEnabled = 16843830; // 0x1010436
@@ -1757,6 +1759,7 @@
field public static final int list = 16908298; // 0x102000a
field public static final int mask = 16908353; // 0x1020041
field public static final int message = 16908299; // 0x102000b
+ field public static final int navigationBarBackground = 16908355; // 0x1020043
field public static final int paste = 16908322; // 0x1020022
field public static final int primary = 16908300; // 0x102000c
field public static final int progress = 16908301; // 0x102000d
@@ -1765,6 +1768,7 @@
field public static final int selectTextMode = 16908333; // 0x102002d
field public static final int selectedIcon = 16908302; // 0x102000e
field public static final int startSelectingText = 16908328; // 0x1020028
+ field public static final int statusBarBackground = 16908354; // 0x1020042
field public static final int stopSelectingText = 16908329; // 0x1020029
field public static final int summary = 16908304; // 0x1020010
field public static final int switchInputMethod = 16908324; // 0x1020024
@@ -5746,7 +5750,7 @@
}
public final class UsageStatsManager {
- method public android.util.ArrayMap<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
+ method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
method public android.app.usage.UsageEvents queryEvents(long, long);
method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
field public static final int INTERVAL_BEST = 4; // 0x4
@@ -7285,8 +7289,8 @@
method public void registerComponentCallbacks(android.content.ComponentCallbacks);
method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
- method public abstract void removeStickyBroadcast(android.content.Intent);
- method public abstract void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
+ method public abstract deprecated void removeStickyBroadcast(android.content.Intent);
+ method public abstract deprecated void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public abstract void revokeUriPermission(android.net.Uri, int);
method public abstract void sendBroadcast(android.content.Intent);
method public abstract void sendBroadcast(android.content.Intent, java.lang.String);
@@ -7295,10 +7299,10 @@
method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String);
method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public abstract void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public abstract void sendStickyBroadcast(android.content.Intent);
- method public abstract void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
- method public abstract void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public abstract void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ method public abstract deprecated void sendStickyBroadcast(android.content.Intent);
+ method public abstract deprecated void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
+ method public abstract deprecated void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ method public abstract deprecated void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public abstract void setTheme(int);
method public abstract deprecated void setWallpaper(android.graphics.Bitmap) throws java.io.IOException;
method public abstract deprecated void setWallpaper(java.io.InputStream) throws java.io.IOException;
@@ -17197,10 +17201,6 @@
field public static final android.os.Parcelable.Creator CREATOR;
}
- public abstract interface NetworkBoundURLFactory {
- method public abstract java.net.URL getBoundURL(android.net.Network, java.net.URL) throws java.net.MalformedURLException;
- }
-
public final class NetworkCapabilities implements android.os.Parcelable {
ctor public NetworkCapabilities(android.net.NetworkCapabilities);
method public int describeContents();
@@ -27196,16 +27196,14 @@
package android.service.voice {
public class AlwaysOnHotwordDetector {
- method public android.content.Intent getManageIntent(int);
+ method public android.content.Intent createIntentToEnroll();
+ method public android.content.Intent createIntentToReEnroll();
+ method public android.content.Intent createIntentToUnEnroll();
method public int getSupportedRecognitionModes();
method public boolean startRecognition(int);
method public boolean stopRecognition();
- field public static final int MANAGE_ACTION_ENROLL = 0; // 0x0
- field public static final int MANAGE_ACTION_RE_ENROLL = 1; // 0x1
- field public static final int MANAGE_ACTION_UN_ENROLL = 2; // 0x2
field public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 2; // 0x2
field public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 1; // 0x1
- field public static final int RECOGNITION_FLAG_NONE = 0; // 0x0
field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2
field public static final int RECOGNITION_MODE_VOICE_TRIGGER = 1; // 0x1
field public static final int STATE_HARDWARE_UNAVAILABLE = -2; // 0xfffffffe
@@ -27214,7 +27212,8 @@
field public static final int STATE_KEYPHRASE_UNSUPPORTED = -1; // 0xffffffff
}
- public static abstract interface AlwaysOnHotwordDetector.Callback {
+ public static abstract class AlwaysOnHotwordDetector.Callback {
+ ctor public AlwaysOnHotwordDetector.Callback();
method public abstract void onAvailabilityChanged(int);
method public abstract void onDetected(android.service.voice.AlwaysOnHotwordDetector.EventPayload);
method public abstract void onError();
@@ -28421,6 +28420,7 @@
}
public static class PhoneAccount.Builder {
+ ctor public PhoneAccount.Builder();
method public android.telecomm.PhoneAccount build();
method public android.telecomm.PhoneAccount.Builder withAccountHandle(android.telecomm.PhoneAccountHandle);
method public android.telecomm.PhoneAccount.Builder withCapabilities(int);
@@ -39590,22 +39590,6 @@
}
-package com.android.internal.telecomm {
-
- public abstract interface RemoteServiceCallback implements android.os.IInterface {
- method public abstract void onError() throws android.os.RemoteException;
- method public abstract void onResult(java.util.List<android.content.ComponentName>, java.util.List<android.os.IBinder>) throws android.os.RemoteException;
- }
-
- public static abstract class RemoteServiceCallback.Stub extends android.os.Binder implements com.android.internal.telecomm.RemoteServiceCallback {
- ctor public RemoteServiceCallback.Stub();
- method public android.os.IBinder asBinder();
- method public static com.android.internal.telecomm.RemoteServiceCallback asInterface(android.os.IBinder);
- method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
- }
-
-}
-
package com.android.internal.util {
public abstract interface Predicate {
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 74ccbc2..6e77e13 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -161,17 +161,17 @@
LOG_ALWAYS_FATAL_IF((numChars >= PATH_MAX || numChars < 0),
"Error constructing dalvik cache : %s", strerror(errno));
- int result = mkdir(dalvikCacheDir, 0771);
+ int result = mkdir(dalvikCacheDir, 0711);
LOG_ALWAYS_FATAL_IF((result < 0 && errno != EEXIST),
"Error creating cache dir %s : %s", dalvikCacheDir, strerror(errno));
// We always perform these steps because the directory might
// already exist, with wider permissions and a different owner
// than we'd like.
- result = chown(dalvikCacheDir, AID_SYSTEM, AID_SYSTEM);
+ result = chown(dalvikCacheDir, AID_ROOT, AID_ROOT);
LOG_ALWAYS_FATAL_IF((result < 0), "Error changing dalvik-cache ownership : %s", strerror(errno));
- result = chmod(dalvikCacheDir, 0771);
+ result = chmod(dalvikCacheDir, 0711);
LOG_ALWAYS_FATAL_IF((result < 0),
"Error changing dalvik-cache permissions : %s", strerror(errno));
}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index d9b40b1..da34094 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -866,6 +866,7 @@
private void runInstall() {
int installFlags = PackageManager.INSTALL_ALL_USERS;
+ int userId = UserHandle.USER_ALL;
String installerPackageName = null;
String opt;
@@ -909,6 +910,13 @@
}
} else if (opt.equals("--abi")) {
abi = checkAbiArgument(nextOptionData());
+ } else if (opt.equals("--user")) {
+ userId = Integer.parseInt(nextOptionData());
+ if (userId == UserHandle.USER_ALL) {
+ installFlags |= PackageManager.INSTALL_ALL_USERS;
+ } else {
+ installFlags &= ~PackageManager.INSTALL_ALL_USERS;
+ }
} else {
System.err.println("Error: Unknown option: " + opt);
return;
@@ -953,8 +961,8 @@
VerificationParams verificationParams = new VerificationParams(verificationURI,
originatingURI, referrerURI, VerificationParams.NO_UID, null);
- mPm.installPackage(apkFilePath, obs.getBinder(), installFlags, installerPackageName,
- verificationParams, abi);
+ mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags, installerPackageName,
+ verificationParams, abi, userId);
synchronized (obs) {
while (!obs.finished) {
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 214f50c..dbd180f 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.net.Uri;
+import android.os.Bundle;
import android.service.notification.Condition;
import android.service.notification.IConditionListener;
import android.service.notification.IConditionProvider;
@@ -58,13 +59,15 @@
void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
void cancelNotificationsFromListener(in INotificationListener token, in String[] keys);
- ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys);
+ ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys, int trim);
void requestHintsFromListener(in INotificationListener token, int hints);
int getHintsFromListener(in INotificationListener token);
void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter);
int getInterruptionFilterFromListener(in INotificationListener token);
+ void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
ComponentName getEffectsSuppressor();
+ boolean matchesCallFilter(in Bundle extras);
ZenModeConfig getZenModeConfig();
boolean setZenModeConfig(in ZenModeConfig config);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e1dc8bf..966d2ce 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1424,6 +1424,8 @@
extras.remove(Notification.EXTRA_LARGE_ICON);
extras.remove(Notification.EXTRA_LARGE_ICON_BIG);
extras.remove(Notification.EXTRA_PICTURE);
+ // Prevent light notifications from being rebuilt.
+ extras.remove(Builder.EXTRA_NEEDS_REBUILD);
}
}
@@ -1868,6 +1870,15 @@
private Notification mRebuildNotification = null;
/**
+ * Whether the build notification has three lines. This is used to make the top padding for
+ * both the contracted and expanded layout consistent.
+ *
+ * <p>
+ * This field is only valid during the build phase.
+ */
+ private boolean mHasThreeLines;
+
+ /**
* Constructs a new Builder with the defaults:
*
@@ -2564,19 +2575,23 @@
return this;
}
- private Bitmap getProfileBadge() {
+ private Drawable getProfileBadgeDrawable() {
// Note: This assumes that the current user can read the profile badge of the
// originating user.
UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- Drawable badge = userManager.getBadgeForUser(new UserHandle(mOriginatingUserId), 0);
+ return userManager.getBadgeForUser(new UserHandle(mOriginatingUserId), 0);
+ }
+
+ private Bitmap getProfileBadge() {
+ Drawable badge = getProfileBadgeDrawable();
if (badge == null) {
return null;
}
- final int width = badge.getIntrinsicWidth();
- final int height = badge.getIntrinsicHeight();
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ final int size = mContext.getResources().getDimensionPixelSize(
+ R.dimen.notification_badge_size);
+ Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
- badge.setBounds(0, 0, width, height);
+ badge.setBounds(0, 0, size, size);
badge.draw(canvas);
return bitmap;
}
@@ -2602,6 +2617,12 @@
return false;
}
+ private void shrinkLine3Text(RemoteViews contentView) {
+ float subTextSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.notification_subtext_size);
+ contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
+ }
+
private RemoteViews applyStandardTemplate(int resId) {
RemoteViews contentView = new BuilderRemoteViews(mContext.getPackageName(),
mOriginatingUserId, resId);
@@ -2674,10 +2695,7 @@
if (showLine2) {
// need to shrink all the type to make sure everything fits
- final Resources res = mContext.getResources();
- final float subTextSize = res.getDimensionPixelSize(
- R.dimen.notification_subtext_size);
- contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
+ shrinkLine3Text(contentView);
}
if (mWhen != 0 && mShowWhen) {
@@ -2696,7 +2714,7 @@
// Adjust padding depending on line count and font size.
contentView.setViewPadding(R.id.line1, 0, calculateTopPadding(mContext,
- hasThreeLines(), mContext.getResources().getConfiguration().fontScale),
+ mHasThreeLines, mContext.getResources().getConfiguration().fontScale),
0, 0);
// We want to add badge to first line of text.
@@ -2721,7 +2739,12 @@
* is going to have one or two lines
*/
private boolean hasThreeLines() {
- boolean hasLine3 = mContentText != null || mContentInfo != null || mNumber > 0;
+ boolean contentTextInLine2 = mSubText != null && mContentText != null;
+
+ // If we have content text in line 2, badge goes into line 2, or line 3 otherwise
+ boolean badgeInLine3 = getProfileBadgeDrawable() != null && !contentTextInLine2;
+ boolean hasLine3 = mContentText != null || mContentInfo != null || mNumber > 0
+ || badgeInLine3;
boolean hasLine2 = (mSubText != null && mContentText != null) ||
(mSubText == null && (mProgressMax != 0 || mProgressIndeterminate));
return hasLine2 && hasLine3;
@@ -3092,6 +3115,7 @@
if (mRebuildNotification == null) {
throw new IllegalStateException("rebuild() only valid when in 'rebuild' mode.");
}
+ mHasThreeLines = hasThreeLines();
Bundle extras = mRebuildNotification.extras;
@@ -3124,6 +3148,7 @@
}
extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW);
+ mHasThreeLines = false;
return mRebuildNotification;
}
@@ -3238,6 +3263,7 @@
*/
public Notification build() {
mOriginatingUserId = mContext.getUserId();
+ mHasThreeLines = hasThreeLines();
Notification n = buildUnstyled();
@@ -3259,6 +3285,7 @@
mStyle.addExtras(n.extras);
}
+ mHasThreeLines = false;
return n;
}
@@ -3388,7 +3415,7 @@
*/
protected void applyTopPadding(RemoteViews contentView) {
int topPadding = Builder.calculateTopPadding(mBuilder.mContext,
- mBuilder.hasThreeLines(),
+ mBuilder.mHasThreeLines,
mBuilder.mContext.getResources().getConfiguration().fontScale);
contentView.setViewPadding(R.id.line1, 0, topPadding, 0, 0);
}
@@ -3661,6 +3688,8 @@
applyTopPadding(contentView);
+ mBuilder.shrinkLine3Text(contentView);
+
mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
return contentView;
@@ -3800,6 +3829,8 @@
applyTopPadding(contentView);
+ mBuilder.shrinkLine3Text(contentView);
+
mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
return contentView;
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index fc047de..7dc1ad64 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -20,6 +20,7 @@
import android.app.Notification.Builder;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -251,5 +252,17 @@
}
}
+ /**
+ * @hide
+ */
+ public boolean matchesCallFilter(Bundle extras) {
+ INotificationManager service = getService();
+ try {
+ return service.matchesCallFilter(extras);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
private Context mContext;
}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 2431ad0..fb80de2 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -106,7 +106,9 @@
}
/**
- * The time at which this event occurred.
+ * The time at which this event occurred, measured in milliseconds since the epoch.
+ * <p/>
+ * See {@link System#currentTimeMillis()}.
*/
public long getTimeStamp() {
return mTimeStamp;
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index e47a802..abfc435 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -81,28 +81,36 @@
}
/**
- * Get the beginning of the time range this {@link android.app.usage.UsageStats} represents.
+ * Get the beginning of the time range this {@link android.app.usage.UsageStats} represents,
+ * measured in milliseconds since the epoch.
+ * <p/>
+ * See {@link System#currentTimeMillis()}.
*/
public long getFirstTimeStamp() {
return mBeginTimeStamp;
}
/**
- * Get the end of the time range this {@link android.app.usage.UsageStats} represents.
+ * Get the end of the time range this {@link android.app.usage.UsageStats} represents,
+ * measured in milliseconds since the epoch.
+ * <p/>
+ * See {@link System#currentTimeMillis()}.
*/
public long getLastTimeStamp() {
return mEndTimeStamp;
}
/**
- * Get the last time this package was used.
+ * Get the last time this package was used, measured in milliseconds since the epoch.
+ * <p/>
+ * See {@link System#currentTimeMillis()}.
*/
public long getLastTimeUsed() {
return mLastTimeUsed;
}
/**
- * Get the total time this package spent in the foreground.
+ * Get the total time this package spent in the foreground, measured in milliseconds.
*/
public long getTotalTimeInForeground() {
return mTotalTimeInForeground;
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index f9b8928..5830fcf 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -23,6 +23,7 @@
import java.util.Collections;
import java.util.List;
+import java.util.Map;
/**
* Provides access to device usage history and statistics. Usage data is aggregated into
@@ -149,7 +150,6 @@
* @param endTime The exclusive end of the range of events to include in the results.
* @return A {@link UsageEvents}.
*/
- @SuppressWarnings("unchecked")
public UsageEvents queryEvents(long beginTime, long endTime) {
try {
UsageEvents iter = mService.queryEvents(beginTime, endTime,
@@ -170,15 +170,13 @@
*
* @param beginTime The inclusive beginning of the range of stats to include in the results.
* @param endTime The exclusive end of the range of stats to include in the results.
- * @return An {@link android.util.ArrayMap} keyed by package name or null if no stats are
+ * @return A {@link java.util.Map} keyed by package name, or null if no stats are
* available.
*/
- public ArrayMap<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) {
+ public Map<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) {
List<UsageStats> stats = queryUsageStats(INTERVAL_BEST, beginTime, endTime);
if (stats.isEmpty()) {
- @SuppressWarnings("unchecked")
- ArrayMap<String, UsageStats> emptyStats = ArrayMap.EMPTY;
- return emptyStats;
+ return Collections.emptyMap();
}
ArrayMap<String, UsageStats> aggregatedStats = new ArrayMap<>();
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b7d7c25..f979a0c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1486,8 +1486,6 @@
* @see #sendBroadcast(Intent)
* @see #sendBroadcast(Intent, String)
* @see #sendOrderedBroadcast(Intent, String)
- * @see #sendStickyBroadcast(Intent)
- * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
* @see android.content.BroadcastReceiver
* @see #registerReceiver
* @see android.app.Activity#RESULT_OK
@@ -1584,7 +1582,7 @@
@Nullable Bundle initialExtras);
/**
- * Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the
+ * <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the
* Intent you are sending stays around after the broadcast is complete,
* so that others can quickly retrieve that data through the return
* value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}. In
@@ -1595,6 +1593,12 @@
* permission in order to use this API. If you do not hold that
* permission, {@link SecurityException} will be thrown.
*
+ * @deprecated Sticky broadcasts should not be used. They provide no security (anyone
+ * can access them), no protection (anyone can modify them), and many other problems.
+ * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+ * has changed, with another mechanism for apps to retrieve the current value whenever
+ * desired.
+ *
* @param intent The Intent to broadcast; all receivers matching this
* Intent will receive the broadcast, and the Intent will be held to
* be re-broadcast to future receivers.
@@ -1602,10 +1606,11 @@
* @see #sendBroadcast(Intent)
* @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
*/
+ @Deprecated
public abstract void sendStickyBroadcast(Intent intent);
/**
- * Version of {@link #sendStickyBroadcast} that allows you to
+ * <p>Version of {@link #sendStickyBroadcast} that allows you to
* receive data back from the broadcast. This is accomplished by
* supplying your own BroadcastReceiver when calling, which will be
* treated as a final receiver at the end of the broadcast -- its
@@ -1622,6 +1627,12 @@
*
* <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
*
+ * @deprecated Sticky broadcasts should not be used. They provide no security (anyone
+ * can access them), no protection (anyone can modify them), and many other problems.
+ * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+ * has changed, with another mechanism for apps to retrieve the current value whenever
+ * desired.
+ *
* @param intent The Intent to broadcast; all receivers matching this
* Intent will receive the broadcast.
* @param resultReceiver Your own BroadcastReceiver to treat as the final
@@ -1644,31 +1655,45 @@
* @see #registerReceiver
* @see android.app.Activity#RESULT_OK
*/
+ @Deprecated
public abstract void sendStickyOrderedBroadcast(Intent intent,
BroadcastReceiver resultReceiver,
@Nullable Handler scheduler, int initialCode, @Nullable String initialData,
@Nullable Bundle initialExtras);
/**
- * Remove the data previously sent with {@link #sendStickyBroadcast},
+ * <p>Remove the data previously sent with {@link #sendStickyBroadcast},
* so that it is as if the sticky broadcast had never happened.
*
* <p>You must hold the {@link android.Manifest.permission#BROADCAST_STICKY}
* permission in order to use this API. If you do not hold that
* permission, {@link SecurityException} will be thrown.
*
+ * @deprecated Sticky broadcasts should not be used. They provide no security (anyone
+ * can access them), no protection (anyone can modify them), and many other problems.
+ * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+ * has changed, with another mechanism for apps to retrieve the current value whenever
+ * desired.
+ *
* @param intent The Intent that was previously broadcast.
*
* @see #sendStickyBroadcast
*/
+ @Deprecated
public abstract void removeStickyBroadcast(Intent intent);
/**
- * Version of {@link #sendStickyBroadcast(Intent)} that allows you to specify the
+ * <p>Version of {@link #sendStickyBroadcast(Intent)} that allows you to specify the
* user the broadcast will be sent to. This is not available to applications
* that are not pre-installed on the system image. Using it requires holding
* the INTERACT_ACROSS_USERS permission.
*
+ * @deprecated Sticky broadcasts should not be used. They provide no security (anyone
+ * can access them), no protection (anyone can modify them), and many other problems.
+ * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+ * has changed, with another mechanism for apps to retrieve the current value whenever
+ * desired.
+ *
* @param intent The Intent to broadcast; all receivers matching this
* Intent will receive the broadcast, and the Intent will be held to
* be re-broadcast to future receivers.
@@ -1676,10 +1701,11 @@
*
* @see #sendBroadcast(Intent)
*/
+ @Deprecated
public abstract void sendStickyBroadcastAsUser(Intent intent, UserHandle user);
/**
- * Version of
+ * <p>Version of
* {@link #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)}
* that allows you to specify the
* user the broadcast will be sent to. This is not available to applications
@@ -1688,6 +1714,12 @@
*
* <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
*
+ * @deprecated Sticky broadcasts should not be used. They provide no security (anyone
+ * can access them), no protection (anyone can modify them), and many other problems.
+ * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+ * has changed, with another mechanism for apps to retrieve the current value whenever
+ * desired.
+ *
* @param intent The Intent to broadcast; all receivers matching this
* Intent will receive the broadcast.
* @param user UserHandle to send the intent to.
@@ -1705,13 +1737,14 @@
*
* @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
*/
+ @Deprecated
public abstract void sendStickyOrderedBroadcastAsUser(Intent intent,
UserHandle user, BroadcastReceiver resultReceiver,
@Nullable Handler scheduler, int initialCode, @Nullable String initialData,
@Nullable Bundle initialExtras);
/**
- * Version of {@link #removeStickyBroadcast(Intent)} that allows you to specify the
+ * <p>Version of {@link #removeStickyBroadcast(Intent)} that allows you to specify the
* user the broadcast will be sent to. This is not available to applications
* that are not pre-installed on the system image. Using it requires holding
* the INTERACT_ACROSS_USERS permission.
@@ -1720,11 +1753,18 @@
* permission in order to use this API. If you do not hold that
* permission, {@link SecurityException} will be thrown.
*
+ * @deprecated Sticky broadcasts should not be used. They provide no security (anyone
+ * can access them), no protection (anyone can modify them), and many other problems.
+ * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+ * has changed, with another mechanism for apps to retrieve the current value whenever
+ * desired.
+ *
* @param intent The Intent that was previously broadcast.
* @param user UserHandle to remove the sticky broadcast from.
*
* @see #sendStickyBroadcastAsUser
*/
+ @Deprecated
public abstract void removeStickyBroadcastAsUser(Intent intent, UserHandle user);
/**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 44478d4..bd1487c 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -198,6 +198,14 @@
in VerificationParams verificationParams,
in String packageAbiOverride);
+ void installPackageAsUser(in String originPath,
+ in IPackageInstallObserver2 observer,
+ int flags,
+ in String installerPackageName,
+ in VerificationParams verificationParams,
+ in String packageAbiOverride,
+ int userId);
+
void finishPackageInstall(int token);
void setInstallerPackageName(in String targetPackage, in String installerPackageName);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index c928a18..44e24b1 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -623,7 +623,7 @@
try {
final ParcelFileDescriptor clientSocket = mSession.openWrite(name,
offsetBytes, lengthBytes);
- return new FileBridge.FileBridgeOutputStream(clientSocket.getFileDescriptor());
+ return new FileBridge.FileBridgeOutputStream(clientSocket);
} catch (RuntimeException e) {
ExceptionUtils.maybeUnwrapIOException(e);
throw e;
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index ec4bc7d..0895fe3 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -19,6 +19,7 @@
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.CaptureResultExtras;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -48,13 +49,23 @@
*/
public final class TotalCaptureResult extends CaptureResult {
+ private final List<CaptureResult> mPartialResults;
+
/**
- * Takes ownership of the passed-in properties object
+ * Takes ownership of the passed-in camera metadata and the partial results
+ *
+ * @param partials a list of partial results; {@code null} will be substituted for an empty list
* @hide
*/
public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent,
- CaptureResultExtras extras) {
+ CaptureResultExtras extras, List<CaptureResult> partials) {
super(results, parent, extras);
+
+ if (partials == null) {
+ mPartialResults = new ArrayList<>();
+ } else {
+ mPartialResults = partials;
+ }
}
/**
@@ -65,6 +76,8 @@
*/
public TotalCaptureResult(CameraMetadataNative results, int sequenceId) {
super(results, sequenceId);
+
+ mPartialResults = new ArrayList<>();
}
/**
@@ -73,14 +86,13 @@
* <p>The list is returned is unmodifiable; attempting to modify it will result in a
* {@code UnsupportedOperationException} being thrown.</p>
*
- * <p>The list size will be inclusive between {@code 1} and
- * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT}, in ascending order
+ * <p>The list size will be inclusive between {@code 0} and
+ * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT}, with elements in ascending order
* of when {@link CameraCaptureSession.CaptureListener#onCaptureProgressed} was invoked.</p>
*
* @return unmodifiable list of partial results
*/
public List<CaptureResult> getPartialResults() {
- // TODO
- return Collections.unmodifiableList(null);
+ return Collections.unmodifiableList(mPartialResults);
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index d75dfe6..f5666bf 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -41,6 +41,7 @@
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -967,6 +968,8 @@
private long mCompletedFrameNumber = -1;
private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
+ /** Map frame numbers to list of partial results */
+ private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>();
private void update() {
Iterator<Long> iter = mFutureErrorSet.iterator();
@@ -983,8 +986,8 @@
/**
* This function is called every time when a result or an error is received.
- * @param frameNumber: the frame number corresponding to the result or error
- * @param isError: true if it is an error, false if it is not an error
+ * @param frameNumber the frame number corresponding to the result or error
+ * @param isError true if it is an error, false if it is not an error
*/
public void updateTracker(long frameNumber, boolean isError) {
if (isError) {
@@ -1006,6 +1009,55 @@
update();
}
+ /**
+ * This function is called every time a result has been completed.
+ *
+ * <p>It keeps a track of all the partial results already created for a particular
+ * frame number.</p>
+ *
+ * @param frameNumber the frame number corresponding to the result
+ * @param result the total or partial result
+ * @param partial {@true} if the result is partial, {@code false} if total
+ */
+ public void updateTracker(long frameNumber, CaptureResult result, boolean partial) {
+
+ if (!partial) {
+ // Update the total result's frame status as being successful
+ updateTracker(frameNumber, /*isError*/false);
+ // Don't keep a list of total results, we don't need to track them
+ return;
+ }
+
+ if (result == null) {
+ // Do not record blank results; this also means there will be no total result
+ // so it doesn't matter that the partials were not recorded
+ return;
+ }
+
+ // Partial results must be aggregated in-order for that frame number
+ List<CaptureResult> partials = mPartialResults.get(frameNumber);
+ if (partials == null) {
+ partials = new ArrayList<>();
+ mPartialResults.put(frameNumber, partials);
+ }
+
+ partials.add(result);
+ }
+
+ /**
+ * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}.
+ *
+ * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker}
+ * is called again with new partials for that frame number).</p>
+ *
+ * @param frameNumber the frame number corresponding to the result
+ * @return a list of partial results for that frame with at least 1 element,
+ * or {@code null} if there were no partials recorded for that frame
+ */
+ public List<CaptureResult> popPartialResults(long frameNumber) {
+ return mPartialResults.remove(frameNumber);
+ }
+
public long getCompletedFrameNumber() {
return mCompletedFrameNumber;
}
@@ -1244,12 +1296,6 @@
boolean isPartialResult =
(resultExtras.getPartialResultCount() < mTotalPartialCount);
- // Update tracker (increment counter) when it's not a partial result.
- if (!isPartialResult) {
- mFrameNumberTracker.updateTracker(frameNumber,
- /*error*/false);
- }
-
// Check if we have a listener for this
if (holder == null) {
if (DEBUG) {
@@ -1257,6 +1303,9 @@
"holder is null, early return at frame "
+ frameNumber);
}
+
+ mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
+
return;
}
@@ -1266,6 +1315,8 @@
"camera is closed, early return at frame "
+ frameNumber);
}
+
+ mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
return;
}
@@ -1273,6 +1324,8 @@
Runnable resultDispatch = null;
+ CaptureResult finalResult;
+
// Either send a partial result or the final capture completed result
if (isPartialResult) {
final CaptureResult resultAsCapture =
@@ -1290,9 +1343,14 @@
}
}
};
+
+ finalResult = resultAsCapture;
} else {
+ List<CaptureResult> partialResults =
+ mFrameNumberTracker.popPartialResults(frameNumber);
+
final TotalCaptureResult resultAsCapture =
- new TotalCaptureResult(result, request, resultExtras);
+ new TotalCaptureResult(result, request, resultExtras, partialResults);
// Final capture result
resultDispatch = new Runnable() {
@@ -1306,10 +1364,15 @@
}
}
};
+
+ finalResult = resultAsCapture;
}
holder.getHandler().post(resultDispatch);
+ // Collect the partials for a total result; or mark the frame as totally completed
+ mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult);
+
// Fire onCaptureSequenceCompleted
if (!isPartialResult) {
checkAndFireSequenceComplete();
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index febb015..f47ce79 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -67,7 +67,6 @@
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
/**
* Implementation of camera metadata marshal/unmarshal across Binder to
@@ -655,6 +654,15 @@
private Face[] getFaces() {
Integer faceDetectMode = get(CaptureResult.STATISTICS_FACE_DETECT_MODE);
+ byte[] faceScores = get(CaptureResult.STATISTICS_FACE_SCORES);
+ Rect[] faceRectangles = get(CaptureResult.STATISTICS_FACE_RECTANGLES);
+ int[] faceIds = get(CaptureResult.STATISTICS_FACE_IDS);
+ int[] faceLandmarks = get(CaptureResult.STATISTICS_FACE_LANDMARKS);
+
+ if (areValuesAllNull(faceDetectMode, faceScores, faceRectangles, faceIds, faceLandmarks)) {
+ return null;
+ }
+
if (faceDetectMode == null) {
Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE");
faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE;
@@ -670,8 +678,6 @@
}
// Face scores and rectangles are required by SIMPLE and FULL mode.
- byte[] faceScores = get(CaptureResult.STATISTICS_FACE_SCORES);
- Rect[] faceRectangles = get(CaptureResult.STATISTICS_FACE_RECTANGLES);
if (faceScores == null || faceRectangles == null) {
Log.w(TAG, "Expect face scores and rectangles to be non-null");
return new Face[0];
@@ -683,8 +689,6 @@
// To be safe, make number of faces is the minimal of all face info metadata length.
int numFaces = Math.min(faceScores.length, faceRectangles.length);
// Face id and landmarks are only required by FULL mode.
- int[] faceIds = get(CaptureResult.STATISTICS_FACE_IDS);
- int[] faceLandmarks = get(CaptureResult.STATISTICS_FACE_LANDMARKS);
if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
if (faceIds == null || faceLandmarks == null) {
Log.w(TAG, "Expect face ids and landmarks to be non-null for FULL mode," +
@@ -755,22 +759,32 @@
private LensShadingMap getLensShadingMap() {
float[] lsmArray = getBase(CaptureResult.STATISTICS_LENS_SHADING_MAP);
+ Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE);
+
+ // Do not warn if lsmArray is null while s is not. This is valid.
if (lsmArray == null) {
- Log.w(TAG, "getLensShadingMap - Lens shading map was null.");
return null;
}
- Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE);
+
+ if (s == null) {
+ Log.w(TAG, "getLensShadingMap - Lens shading map size was null.");
+ return null;
+ }
+
LensShadingMap map = new LensShadingMap(lsmArray, s.getHeight(), s.getWidth());
return map;
}
private Location getGpsLocation() {
String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
- Location l = new Location(translateProcessToLocationProvider(processingMethod));
-
double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES);
Long timeStamp = get(CaptureResult.JPEG_GPS_TIMESTAMP);
+ if (areValuesAllNull(processingMethod, coords, timeStamp)) {
+ return null;
+ }
+
+ Location l = new Location(translateProcessToLocationProvider(processingMethod));
if (timeStamp != null) {
l.setTime(timeStamp);
} else {
@@ -873,7 +887,13 @@
float[] red = getBase(CaptureRequest.TONEMAP_CURVE_RED);
float[] green = getBase(CaptureRequest.TONEMAP_CURVE_GREEN);
float[] blue = getBase(CaptureRequest.TONEMAP_CURVE_BLUE);
+
+ if (areValuesAllNull(red, green, blue)) {
+ return null;
+ }
+
if (red == null || green == null || blue == null) {
+ Log.w(TAG, "getTonemapCurve - missing tone curve components");
return null;
}
TonemapCurve tc = new TonemapCurve(red, green, blue);
@@ -1208,6 +1228,18 @@
}
}
+ /** Check if input arguments are all {@code null}.
+ *
+ * @param objs Input arguments for null check
+ * @return {@code true} if input arguments are all {@code null}, otherwise {@code false}
+ */
+ private static boolean areValuesAllNull(Object... objs) {
+ for (Object o : objs) {
+ if (o != null) return false;
+ }
+ return true;
+ }
+
static {
/*
* We use a class initializer to allow the native code to cache some field offsets
diff --git a/core/java/android/hardware/hdmi/HdmiPortInfo.java b/core/java/android/hardware/hdmi/HdmiPortInfo.java
index 85e7531..2ec6126 100644
--- a/core/java/android/hardware/hdmi/HdmiPortInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiPortInfo.java
@@ -166,7 +166,7 @@
public String toString() {
StringBuffer s = new StringBuffer();
s.append("port_id: ").append(mId).append(", ");
- s.append("address: ").append(mAddress).append(", ");
+ s.append("address: ").append(String.format("0x%04x", mAddress)).append(", ");
s.append("cec: ").append(mCecSupported).append(", ");
s.append("arc: ").append(mArcSupported).append(", ");
s.append("mhl: ").append(mMhlSupported);
diff --git a/core/java/android/net/NetworkBoundURLFactory.java b/core/java/android/net/NetworkBoundURLFactory.java
deleted file mode 100644
index 356100e..0000000
--- a/core/java/android/net/NetworkBoundURLFactory.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-
-/**
- * An interface that describes a factory for network-specific {@link URL} objects.
- */
-public interface NetworkBoundURLFactory {
- /**
- * Returns a {@link URL} based on the given URL but bound to the specified {@code Network},
- * such that opening the URL will send all network traffic on the specified Network.
- *
- * @return a {@link URL} bound to this {@code Network}.
- * @throws MalformedURLException if the URL was not valid, or this factory cannot handle the
- * specified URL (e.g., if it does not support the protocol of the URL).
- */
- public URL getBoundURL(Network network, URL url) throws MalformedURLException;
-}
diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java
index bf8d15c..022a106 100644
--- a/core/java/android/os/FileBridge.java
+++ b/core/java/android/os/FileBridge.java
@@ -131,10 +131,17 @@
}
public static class FileBridgeOutputStream extends OutputStream {
+ private final ParcelFileDescriptor mClientPfd;
private final FileDescriptor mClient;
private final byte[] mTemp = new byte[MSG_LENGTH];
+ public FileBridgeOutputStream(ParcelFileDescriptor clientPfd) {
+ mClientPfd = clientPfd;
+ mClient = clientPfd.getFileDescriptor();
+ }
+
public FileBridgeOutputStream(FileDescriptor client) {
+ mClientPfd = null;
mClient = client;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ae11f47..33e0468 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -904,6 +904,15 @@
public static final String ACTION_APP_NOTIFICATION_SETTINGS
= "android.settings.APP_NOTIFICATION_SETTINGS";
+ /**
+ * Activity Action: Show notification redaction settings.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_APP_NOTIFICATION_REDACTION
+ = "android.settings.ACTION_APP_NOTIFICATION_REDACTION";
+
/** @hide */ public static final String EXTRA_APP_UID = "app_uid";
/** @hide */ public static final String EXTRA_APP_PACKAGE = "app_package";
@@ -3714,6 +3723,14 @@
"lock_screen_allow_private_notifications";
/**
+ * Set by the system to track if the user needs to see the call to action for
+ * the lockscreen notification policy.
+ * @hide
+ */
+ public static final String SHOW_NOTE_ABOUT_NOTIFICATION_HIDING =
+ "show_note_about_notification_hiding";
+
+ /**
* The Logging ID (a unique 64-bit value) as a hex string.
* Used as a pseudonymous identifier for logging.
* @deprecated This identifier is poorly initialized and has
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index a544b2d..cb0bcf2 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -81,6 +81,33 @@
* This does not change the interruption filter, only the effects. **/
public static final int HINT_HOST_DISABLE_EFFECTS = 1;
+ /**
+ * The full trim of the StatusBarNotification including all its features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int TRIM_FULL = 0;
+
+ /**
+ * A light trim of the StatusBarNotification excluding the following features:
+ *
+ * <ol>
+ * <li>{@link Notification#tickerView tickerView}</li>
+ * <li>{@link Notification#contentView contentView}</li>
+ * <li>{@link Notification#largeIcon largeIcon}</li>
+ * <li>{@link Notification#bigContentView bigContentView}</li>
+ * <li>{@link Notification#headsUpContentView headsUpContentView}</li>
+ * <li>{@link Notification#EXTRA_LARGE_ICON extras[EXTRA_LARGE_ICON]}</li>
+ * <li>{@link Notification#EXTRA_LARGE_ICON_BIG extras[EXTRA_LARGE_ICON_BIG]}</li>
+ * <li>{@link Notification#EXTRA_PICTURE extras[EXTRA_PICTURE]}</li>
+ * </ol>
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int TRIM_LIGHT = 1;
+
private INotificationListenerWrapper mWrapper = null;
private RankingMap mRankingMap;
@@ -314,13 +341,53 @@
}
/**
+ * Sets the notification trim that will be received via {@link #onNotificationPosted}.
+ *
+ * <p>
+ * Setting a trim other than {@link #TRIM_FULL} enables listeners that don't need access to the
+ * full notification features right away to reduce their memory footprint. Full notifications
+ * can be requested on-demand via {@link #getActiveNotifications(int)}.
+ *
+ * <p>
+ * Set to {@link #TRIM_FULL} initially.
+ *
+ * @hide
+ *
+ * @param trim trim of the notifications to be passed via {@link #onNotificationPosted}.
+ * See <code>TRIM_*</code> constants.
+ */
+ @SystemApi
+ public final void setOnNotificationPostedTrim(int trim) {
+ if (!isBound()) return;
+ try {
+ getNotificationInterface().setOnNotificationPostedTrimFromListener(mWrapper, trim);
+ } catch (RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
+
+ /**
* Request the list of outstanding notifications (that is, those that are visible to the
* current user). Useful when you don't know what's already been posted.
*
* @return An array of active notifications, sorted in natural order.
*/
public StatusBarNotification[] getActiveNotifications() {
- return getActiveNotifications(null);
+ return getActiveNotifications(null, TRIM_FULL);
+ }
+
+ /**
+ * Request the list of outstanding notifications (that is, those that are visible to the
+ * current user). Useful when you don't know what's already been posted.
+ *
+ * @hide
+ *
+ * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants.
+ * @return An array of active notifications, sorted in natural order.
+ */
+ @SystemApi
+ public StatusBarNotification[] getActiveNotifications(int trim) {
+ return getActiveNotifications(null, trim);
}
/**
@@ -328,14 +395,33 @@
* notifications but didn't want to retain the bits, and now need to go back and extract
* more data out of those notifications.
*
+ * @param keys the keys of the notifications to request
* @return An array of notifications corresponding to the requested keys, in the
* same order as the key list.
*/
public StatusBarNotification[] getActiveNotifications(String[] keys) {
- if (!isBound()) return null;
+ return getActiveNotifications(keys, TRIM_FULL);
+ }
+
+ /**
+ * Request one or more notifications by key. Useful if you have been keeping track of
+ * notifications but didn't want to retain the bits, and now need to go back and extract
+ * more data out of those notifications.
+ *
+ * @hide
+ *
+ * @param keys the keys of the notifications to request
+ * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants.
+ * @return An array of notifications corresponding to the requested keys, in the
+ * same order as the key list.
+ */
+ @SystemApi
+ public StatusBarNotification[] getActiveNotifications(String[] keys, int trim) {
+ if (!isBound())
+ return null;
try {
- ParceledListSlice<StatusBarNotification> parceledList =
- getNotificationInterface().getActiveNotificationsFromListener(mWrapper, keys);
+ ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface()
+ .getActiveNotificationsFromListener(mWrapper, keys, trim);
List<StatusBarNotification> list = parceledList.getList();
int N = list.size();
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 2095773..519bc28 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Activity;
import android.content.Intent;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
@@ -84,20 +85,31 @@
private static final int STATE_NOT_READY = 0;
// Keyphrase management actions. Used in getManageIntent() ----//
- /** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
MANAGE_ACTION_ENROLL,
MANAGE_ACTION_RE_ENROLL,
MANAGE_ACTION_UN_ENROLL
})
- public @interface ManageActions {}
+ private @interface ManageActions {}
- /** Indicates that we need to enroll. */
+ /**
+ * Indicates that we need to enroll.
+ *
+ * @hide
+ */
public static final int MANAGE_ACTION_ENROLL = 0;
- /** Indicates that we need to re-enroll. */
+ /**
+ * Indicates that we need to re-enroll.
+ *
+ * @hide
+ */
public static final int MANAGE_ACTION_RE_ENROLL = 1;
- /** Indicates that we need to un-enroll. */
+ /**
+ * Indicates that we need to un-enroll.
+ *
+ * @hide
+ */
public static final int MANAGE_ACTION_UN_ENROLL = 2;
//-- Flags for startRecognition ----//
@@ -111,7 +123,11 @@
})
public @interface RecognitionFlags {}
- /** Empty flag for {@link #startRecognition(int)}. */
+ /**
+ * Empty flag for {@link #startRecognition(int)}.
+ *
+ * @hide
+ */
public static final int RECOGNITION_FLAG_NONE = 0;
/**
* Recognition flag for {@link #startRecognition(int)} that indicates
@@ -264,7 +280,7 @@
/**
* Callbacks for always-on hotword detection.
*/
- public interface Callback {
+ public static abstract class Callback {
/**
* Called when the hotword availability changes.
* This indicates a change in the availability of recognition for the given keyphrase.
@@ -278,7 +294,7 @@
* @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNENROLLED
* @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_ENROLLED
*/
- void onAvailabilityChanged(int status);
+ public abstract void onAvailabilityChanged(int status);
/**
* Called when the keyphrase is spoken.
* This implicitly stops listening for the keyphrase once it's detected.
@@ -289,23 +305,23 @@
* This may contain the trigger audio, if requested when calling
* {@link AlwaysOnHotwordDetector#startRecognition(int)}.
*/
- void onDetected(@NonNull EventPayload eventPayload);
+ public abstract void onDetected(@NonNull EventPayload eventPayload);
/**
* Called when the detection fails due to an error.
*/
- void onError();
+ public abstract void onError();
/**
* Called when the recognition is paused temporarily for some reason.
* This is an informational callback, and the clients shouldn't be doing anything here
* except showing an indication on their UI if they have to.
*/
- void onRecognitionPaused();
+ public abstract void onRecognitionPaused();
/**
* Called when the recognition is resumed after it was temporarily paused.
* This is an informational callback, and the clients shouldn't be doing anything here
* except showing an indication on their UI if they have to.
*/
- void onRecognitionResumed();
+ public abstract void onRecognitionResumed();
}
/**
@@ -372,10 +388,10 @@
/**
* Starts recognition for the associated keyphrase.
*
+ * @see #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
+ * @see #RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS
+ *
* @param recognitionFlags The flags to control the recognition properties.
- * The allowed flags are {@link #RECOGNITION_FLAG_NONE},
- * {@link #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO} and
- * {@link #RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS}.
* @return Indicates whether the call succeeded or not.
* @throws UnsupportedOperationException if the recognition isn't supported.
* Callers should only call this method after a supported state callback on
@@ -430,12 +446,13 @@
}
/**
- * Gets an intent to manage the associated keyphrase.
+ * Creates an intent to start the enrollment for the associated keyphrase.
+ * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+ * Starting re-enrollment is only valid if the keyphrase is un-enrolled,
+ * i.e. {@link #STATE_KEYPHRASE_UNENROLLED},
+ * otherwise {@link #createIntentToReEnroll()} should be preferred.
*
- * @param action The manage action that needs to be performed.
- * One of {@link #MANAGE_ACTION_ENROLL}, {@link #MANAGE_ACTION_RE_ENROLL} or
- * {@link #MANAGE_ACTION_UN_ENROLL}.
- * @return An {@link Intent} to manage the given keyphrase.
+ * @return An {@link Intent} to start enrollment for the given keyphrase.
* @throws UnsupportedOperationException if managing they keyphrase isn't supported.
* Callers should only call this method after a supported state callback on
* {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
@@ -443,10 +460,52 @@
* This may happen if another detector has been instantiated or the
* {@link VoiceInteractionService} hosting this detector has been shut down.
*/
- public Intent getManageIntent(@ManageActions int action) {
- if (DBG) Slog.d(TAG, "getManageIntent(" + action + ")");
+ public Intent createIntentToEnroll() {
+ if (DBG) Slog.d(TAG, "createIntentToEnroll");
synchronized (mLock) {
- return getManageIntentLocked(action);
+ return getManageIntentLocked(MANAGE_ACTION_ENROLL);
+ }
+ }
+
+ /**
+ * Creates an intent to start the un-enrollment for the associated keyphrase.
+ * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+ * Starting re-enrollment is only valid if the keyphrase is already enrolled,
+ * i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error.
+ *
+ * @return An {@link Intent} to start un-enrollment for the given keyphrase.
+ * @throws UnsupportedOperationException if managing they keyphrase isn't supported.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+ * @throws IllegalStateException if the detector is in an invalid state.
+ * This may happen if another detector has been instantiated or the
+ * {@link VoiceInteractionService} hosting this detector has been shut down.
+ */
+ public Intent createIntentToUnEnroll() {
+ if (DBG) Slog.d(TAG, "createIntentToUnEnroll");
+ synchronized (mLock) {
+ return getManageIntentLocked(MANAGE_ACTION_UN_ENROLL);
+ }
+ }
+
+ /**
+ * Creates an intent to start the re-enrollment for the associated keyphrase.
+ * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+ * Starting re-enrollment is only valid if the keyphrase is already enrolled,
+ * i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error.
+ *
+ * @return An {@link Intent} to start re-enrollment for the given keyphrase.
+ * @throws UnsupportedOperationException if managing they keyphrase isn't supported.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+ * @throws IllegalStateException if the detector is in an invalid state.
+ * This may happen if another detector has been instantiated or the
+ * {@link VoiceInteractionService} hosting this detector has been shut down.
+ */
+ public Intent createIntentToReEnroll() {
+ if (DBG) Slog.d(TAG, "createIntentToReEnroll");
+ synchronized (mLock) {
+ return getManageIntentLocked(MANAGE_ACTION_RE_ENROLL);
}
}
@@ -462,12 +521,6 @@
"Managing the given keyphrase is not supported");
}
- if (action != MANAGE_ACTION_ENROLL
- && action != MANAGE_ACTION_RE_ENROLL
- && action != MANAGE_ACTION_UN_ENROLL) {
- throw new IllegalArgumentException("Invalid action specified " + action);
- }
-
return mKeyphraseEnrollmentInfo.getManageKeyphraseIntent(action, mText, mLocale);
}
diff --git a/core/java/android/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java
index ec79b36..3a63805 100644
--- a/core/java/android/text/format/TimeFormatter.java
+++ b/core/java/android/text/format/TimeFormatter.java
@@ -22,8 +22,7 @@
import android.content.res.Resources;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
+import java.nio.CharBuffer;
import java.util.Formatter;
import java.util.Locale;
import java.util.TimeZone;
@@ -31,15 +30,13 @@
import libcore.util.ZoneInfo;
/**
- * Formatting logic for {@link Time}. Contains a port of Bionic's broken strftime_tz to Java. The
- * main issue with this implementation is the treatment of characters as ASCII, despite returning
- * localized (UTF-16) strings from the LocaleData.
+ * Formatting logic for {@link Time}. Contains a port of Bionic's broken strftime_tz to Java.
*
* <p>This class is not thread safe.
*/
class TimeFormatter {
- // An arbitrary value outside the range representable by a byte / ASCII character code.
- private static final int FORCE_LOWER_CASE = 0x100;
+ // An arbitrary value outside the range representable by a char.
+ private static final int FORCE_LOWER_CASE = -1;
private static final int SECSPERMIN = 60;
private static final int MINSPERHOUR = 60;
@@ -62,10 +59,9 @@
private final String dateTimeFormat;
private final String timeOnlyFormat;
private final String dateOnlyFormat;
- private final Locale locale;
private StringBuilder outputBuilder;
- private Formatter outputFormatter;
+ private Formatter numberFormatter;
public TimeFormatter() {
synchronized (TimeFormatter.class) {
@@ -84,7 +80,6 @@
this.dateTimeFormat = sDateTimeFormat;
this.timeOnlyFormat = sTimeOnlyFormat;
this.dateOnlyFormat = sDateOnlyFormat;
- this.locale = locale;
localeData = sLocaleData;
}
}
@@ -97,19 +92,21 @@
StringBuilder stringBuilder = new StringBuilder();
outputBuilder = stringBuilder;
- outputFormatter = new Formatter(stringBuilder, locale);
+ // This uses the US locale because number localization is handled separately (see below)
+ // and locale sensitive strings are output directly using outputBuilder.
+ numberFormatter = new Formatter(stringBuilder, Locale.US);
formatInternal(pattern, wallTime, zoneInfo);
String result = stringBuilder.toString();
// This behavior is the source of a bug since some formats are defined as being
- // in ASCII. Generally localization is very broken.
+ // in ASCII and not localized.
if (localeData.zeroDigit != '0') {
result = localizeDigits(result);
}
return result;
} finally {
outputBuilder = null;
- outputFormatter = null;
+ numberFormatter = null;
}
}
@@ -132,38 +129,30 @@
* {@link #outputBuilder}.
*/
private void formatInternal(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) {
- // Convert to ASCII bytes to be compatible with old implementation behavior.
- byte[] bytes = pattern.getBytes(StandardCharsets.US_ASCII);
- if (bytes.length == 0) {
- return;
- }
-
- ByteBuffer formatBuffer = ByteBuffer.wrap(bytes);
+ CharBuffer formatBuffer = CharBuffer.wrap(pattern);
while (formatBuffer.remaining() > 0) {
- boolean outputCurrentByte = true;
- char currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
- if (currentByteAsChar == '%') {
- outputCurrentByte = handleToken(formatBuffer, wallTime, zoneInfo);
+ boolean outputCurrentChar = true;
+ char currentChar = formatBuffer.get(formatBuffer.position());
+ if (currentChar == '%') {
+ outputCurrentChar = handleToken(formatBuffer, wallTime, zoneInfo);
}
- if (outputCurrentByte) {
- currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
- outputBuilder.append(currentByteAsChar);
+ if (outputCurrentChar) {
+ outputBuilder.append(formatBuffer.get(formatBuffer.position()));
}
-
formatBuffer.position(formatBuffer.position() + 1);
}
}
- private boolean handleToken(ByteBuffer formatBuffer, ZoneInfo.WallTime wallTime,
+ private boolean handleToken(CharBuffer formatBuffer, ZoneInfo.WallTime wallTime,
ZoneInfo zoneInfo) {
- // The byte at formatBuffer.position() is expected to be '%' at this point.
+ // The char at formatBuffer.position() is expected to be '%' at this point.
int modifier = 0;
while (formatBuffer.remaining() > 1) {
- // Increment the position then get the new current byte.
+ // Increment the position then get the new current char.
formatBuffer.position(formatBuffer.position() + 1);
- char currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
- switch (currentByteAsChar) {
+ char currentChar = formatBuffer.get(formatBuffer.position());
+ switch (currentChar) {
case 'A':
modifyAndAppend((wallTime.getWeekDay() < 0
|| wallTime.getWeekDay() >= DAYSPERWEEK)
@@ -206,7 +195,7 @@
formatInternal("%m/%d/%y", wallTime, zoneInfo);
return false;
case 'd':
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
wallTime.getMonthDay());
return false;
case 'E':
@@ -218,46 +207,46 @@
case '0':
case '^':
case '#':
- modifier = currentByteAsChar;
+ modifier = currentChar;
continue;
case 'e':
- outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
+ numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
wallTime.getMonthDay());
return false;
case 'F':
formatInternal("%Y-%m-%d", wallTime, zoneInfo);
return false;
case 'H':
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
wallTime.getHour());
return false;
case 'I':
int hour = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12;
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), hour);
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), hour);
return false;
case 'j':
int yearDay = wallTime.getYearDay() + 1;
- outputFormatter.format(getFormat(modifier, "%03d", "%3d", "%d", "%03d"),
+ numberFormatter.format(getFormat(modifier, "%03d", "%3d", "%d", "%03d"),
yearDay);
return false;
case 'k':
- outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
+ numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
wallTime.getHour());
return false;
case 'l':
int n2 = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12;
- outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), n2);
+ numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), n2);
return false;
case 'M':
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
wallTime.getMinute());
return false;
case 'm':
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
wallTime.getMonth() + 1);
return false;
case 'n':
- modifyAndAppend("\n", modifier);
+ outputBuilder.append('\n');
return false;
case 'p':
modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
@@ -274,27 +263,27 @@
formatInternal("%I:%M:%S %p", wallTime, zoneInfo);
return false;
case 'S':
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
wallTime.getSecond());
return false;
case 's':
int timeInSeconds = wallTime.mktime(zoneInfo);
- modifyAndAppend(Integer.toString(timeInSeconds), modifier);
+ outputBuilder.append(Integer.toString(timeInSeconds));
return false;
case 'T':
formatInternal("%H:%M:%S", wallTime, zoneInfo);
return false;
case 't':
- modifyAndAppend("\t", modifier);
+ outputBuilder.append('\t');
return false;
case 'U':
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
(wallTime.getYearDay() + DAYSPERWEEK - wallTime.getWeekDay())
/ DAYSPERWEEK);
return false;
case 'u':
int day = (wallTime.getWeekDay() == 0) ? DAYSPERWEEK : wallTime.getWeekDay();
- outputFormatter.format("%d", day);
+ numberFormatter.format("%d", day);
return false;
case 'V': /* ISO 8601 week number */
case 'G': /* ISO 8601 year (four digits) */
@@ -326,9 +315,9 @@
--year;
yday += isLeap(year) ? DAYSPERLYEAR : DAYSPERNYEAR;
}
- if (currentByteAsChar == 'V') {
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), w);
- } else if (currentByteAsChar == 'g') {
+ if (currentChar == 'V') {
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), w);
+ } else if (currentChar == 'g') {
outputYear(year, false, true, modifier);
} else {
outputYear(year, true, true, modifier);
@@ -342,10 +331,10 @@
int n = (wallTime.getYearDay() + DAYSPERWEEK - (
wallTime.getWeekDay() != 0 ? (wallTime.getWeekDay() - 1)
: (DAYSPERWEEK - 1))) / DAYSPERWEEK;
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
return false;
case 'w':
- outputFormatter.format("%d", wallTime.getWeekDay());
+ numberFormatter.format("%d", wallTime.getWeekDay());
return false;
case 'X':
formatInternal(timeOnlyFormat, wallTime, zoneInfo);
@@ -371,17 +360,17 @@
return false;
}
int diff = wallTime.getGmtOffset();
- String sign;
+ char sign;
if (diff < 0) {
- sign = "-";
+ sign = '-';
diff = -diff;
} else {
- sign = "+";
+ sign = '+';
}
- modifyAndAppend(sign, modifier);
+ outputBuilder.append(sign);
diff /= SECSPERMIN;
diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR);
- outputFormatter.format(getFormat(modifier, "%04d", "%4d", "%d", "%04d"), diff);
+ numberFormatter.format(getFormat(modifier, "%04d", "%4d", "%d", "%04d"), diff);
return false;
}
case '+':
@@ -422,7 +411,6 @@
break;
default:
outputBuilder.append(str);
-
}
}
@@ -443,14 +431,14 @@
}
if (outputTop) {
if (lead == 0 && trail < 0) {
- modifyAndAppend("-0", modifier);
+ outputBuilder.append("-0");
} else {
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), lead);
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), lead);
}
}
if (outputBottom) {
int n = ((trail < 0) ? -trail : trail);
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
}
}
@@ -472,24 +460,24 @@
}
/**
- * A broken implementation of {@link Character#isUpperCase(char)} that assumes ASCII in order to
- * be compatible with the old native implementation.
+ * A broken implementation of {@link Character#isUpperCase(char)} that assumes ASCII codes in
+ * order to be compatible with the old native implementation.
*/
private static boolean brokenIsUpper(char toCheck) {
return toCheck >= 'A' && toCheck <= 'Z';
}
/**
- * A broken implementation of {@link Character#isLowerCase(char)} that assumes ASCII in order to
- * be compatible with the old native implementation.
+ * A broken implementation of {@link Character#isLowerCase(char)} that assumes ASCII codes in
+ * order to be compatible with the old native implementation.
*/
private static boolean brokenIsLower(char toCheck) {
return toCheck >= 'a' && toCheck <= 'z';
}
/**
- * A broken implementation of {@link Character#toLowerCase(char)} that assumes ASCII in order to
- * be compatible with the old native implementation.
+ * A broken implementation of {@link Character#toLowerCase(char)} that assumes ASCII codes in
+ * order to be compatible with the old native implementation.
*/
private static char brokenToLower(char input) {
if (input >= 'A' && input <= 'Z') {
@@ -499,8 +487,8 @@
}
/**
- * A broken implementation of {@link Character#toUpperCase(char)} that assumes ASCII in order to
- * be compatible with the old native implementation.
+ * A broken implementation of {@link Character#toUpperCase(char)} that assumes ASCII codes in
+ * order to be compatible with the old native implementation.
*/
private static char brokenToUpper(char input) {
if (input >= 'a' && input <= 'z') {
@@ -509,11 +497,4 @@
return input;
}
- /**
- * Safely convert a byte containing an ASCII character to a char, even for character codes
- * > 127.
- */
- private static char convertToChar(byte b) {
- return (char) (b & 0xFF);
- }
}
diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java
index ebb1a5c..eb17429 100644
--- a/core/java/android/transition/ChangeBounds.java
+++ b/core/java/android/transition/ChangeBounds.java
@@ -31,6 +31,7 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.util.IntProperty;
import android.util.Property;
import android.view.View;
import android.view.ViewGroup;
@@ -185,25 +186,36 @@
}
if (numChanges > 0) {
if (!mResizeClip) {
- if (startLeft != endLeft) view.setLeft(startLeft);
- if (startTop != endTop) view.setTop(startTop);
- if (startRight != endRight) view.setRight(startRight);
- if (startBottom != endBottom) view.setBottom(startBottom);
- ObjectAnimator topLeftAnimator = null;
- if (startLeft != endLeft || startTop != endTop) {
- Path topLeftPath = getPathMotion().getPath(startLeft, startTop,
- endLeft, endTop);
- topLeftAnimator = ObjectAnimator.ofInt(view, "left", "top", topLeftPath);
+ Animator anim;
+ if (startWidth == endWidth && startHeight == endHeight) {
+ view.offsetLeftAndRight(startLeft - view.getLeft());
+ view.offsetTopAndBottom(startTop - view.getTop());
+ Path positionPath = getPathMotion().getPath(0, 0, endLeft - startLeft,
+ endTop - startTop);
+ anim = ObjectAnimator.ofInt(view, new HorizontalOffsetProperty(),
+ new VerticalOffsetProperty(), positionPath);
+ } else {
+ if (startLeft != endLeft) view.setLeft(startLeft);
+ if (startTop != endTop) view.setTop(startTop);
+ if (startRight != endRight) view.setRight(startRight);
+ if (startBottom != endBottom) view.setBottom(startBottom);
+ ObjectAnimator topLeftAnimator = null;
+ if (startLeft != endLeft || startTop != endTop) {
+ Path topLeftPath = getPathMotion().getPath(startLeft, startTop,
+ endLeft, endTop);
+ topLeftAnimator = ObjectAnimator
+ .ofInt(view, "left", "top", topLeftPath);
+ }
+ ObjectAnimator bottomRightAnimator = null;
+ if (startRight != endRight || startBottom != endBottom) {
+ Path bottomRightPath = getPathMotion().getPath(startRight, startBottom,
+ endRight, endBottom);
+ bottomRightAnimator = ObjectAnimator.ofInt(view, "right", "bottom",
+ bottomRightPath);
+ }
+ anim = TransitionUtils.mergeAnimators(topLeftAnimator,
+ bottomRightAnimator);
}
- ObjectAnimator bottomRightAnimator = null;
- if (startRight != endRight || startBottom != endBottom) {
- Path bottomRightPath = getPathMotion().getPath(startRight, startBottom,
- endRight, endBottom);
- bottomRightAnimator = ObjectAnimator.ofInt(view, "right", "bottom",
- bottomRightPath);
- }
- Animator anim = TransitionUtils.mergeAnimators(topLeftAnimator,
- bottomRightAnimator);
if (view.getParent() instanceof ViewGroup) {
final ViewGroup parent = (ViewGroup) view.getParent();
parent.suppressLayout(true);
@@ -341,4 +353,48 @@
}
return null;
}
+
+ private abstract static class OffsetProperty extends IntProperty<View> {
+ int mPreviousValue;
+
+ public OffsetProperty(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setValue(View view, int value) {
+ int offset = value - mPreviousValue;
+ offsetBy(view, offset);
+ mPreviousValue = value;
+ }
+
+ @Override
+ public Integer get(View object) {
+ return null;
+ }
+
+ protected abstract void offsetBy(View view, int by);
+ }
+
+ private static class HorizontalOffsetProperty extends OffsetProperty {
+ public HorizontalOffsetProperty() {
+ super("offsetLeftAndRight");
+ }
+
+ @Override
+ protected void offsetBy(View view, int by) {
+ view.offsetLeftAndRight(by);
+ }
+ }
+
+ private static class VerticalOffsetProperty extends OffsetProperty {
+ public VerticalOffsetProperty() {
+ super("offsetTopAndBottom");
+ }
+
+ @Override
+ protected void offsetBy(View view, int by) {
+ view.offsetTopAndBottom(by);
+ }
+ }
}
diff --git a/core/java/android/util/Spline.java b/core/java/android/util/Spline.java
index ed027eb..41a2e5d 100644
--- a/core/java/android/util/Spline.java
+++ b/core/java/android/util/Spline.java
@@ -20,15 +20,34 @@
* Performs spline interpolation given a set of control points.
* @hide
*/
-public final class Spline {
- private final float[] mX;
- private final float[] mY;
- private final float[] mM;
+public abstract class Spline {
- private Spline(float[] x, float[] y, float[] m) {
- mX = x;
- mY = y;
- mM = m;
+ /**
+ * Interpolates the value of Y = f(X) for given X.
+ * Clamps X to the domain of the spline.
+ *
+ * @param x The X value.
+ * @return The interpolated Y = f(X) value.
+ */
+ public abstract float interpolate(float x);
+
+ /**
+ * Creates an appropriate spline based on the properties of the control points.
+ *
+ * If the control points are monotonic then the resulting spline will preserve that and
+ * otherwise optimize for error bounds.
+ */
+ public static Spline createSpline(float[] x, float[] y) {
+ if (!isStrictlyIncreasing(x)) {
+ throw new IllegalArgumentException("The control points must all have strictly "
+ + "increasing X values.");
+ }
+
+ if (isMonotonic(y)) {
+ return createMonotoneCubicSpline(x, y);
+ } else {
+ return createLinearSpline(x, y);
+ }
}
/**
@@ -50,107 +69,229 @@
* @throws IllegalArgumentException if the control points are not monotonic.
*/
public static Spline createMonotoneCubicSpline(float[] x, float[] y) {
- if (x == null || y == null || x.length != y.length || x.length < 2) {
- throw new IllegalArgumentException("There must be at least two control "
- + "points and the arrays must be of equal length.");
- }
-
- final int n = x.length;
- float[] d = new float[n - 1]; // could optimize this out
- float[] m = new float[n];
-
- // Compute slopes of secant lines between successive points.
- for (int i = 0; i < n - 1; i++) {
- float h = x[i + 1] - x[i];
- if (h <= 0f) {
- throw new IllegalArgumentException("The control points must all "
- + "have strictly increasing X values.");
- }
- d[i] = (y[i + 1] - y[i]) / h;
- }
-
- // Initialize the tangents as the average of the secants.
- m[0] = d[0];
- for (int i = 1; i < n - 1; i++) {
- m[i] = (d[i - 1] + d[i]) * 0.5f;
- }
- m[n - 1] = d[n - 2];
-
- // Update the tangents to preserve monotonicity.
- for (int i = 0; i < n - 1; i++) {
- if (d[i] == 0f) { // successive Y values are equal
- m[i] = 0f;
- m[i + 1] = 0f;
- } else {
- float a = m[i] / d[i];
- float b = m[i + 1] / d[i];
- if (a < 0f || b < 0f) {
- throw new IllegalArgumentException("The control points must have "
- + "monotonic Y values.");
- }
- float h = FloatMath.hypot(a, b);
- if (h > 9f) {
- float t = 3f / h;
- m[i] = t * a * d[i];
- m[i + 1] = t * b * d[i];
- }
- }
- }
- return new Spline(x, y, m);
+ return new MonotoneCubicSpline(x, y);
}
/**
- * Interpolates the value of Y = f(X) for given X.
- * Clamps X to the domain of the spline.
+ * Creates a linear spline from a given set of control points.
*
- * @param x The X value.
- * @return The interpolated Y = f(X) value.
+ * Like a monotone cubic spline, the interpolated curve will be monotonic if the control points
+ * are monotonic.
+ *
+ * @param x The X component of the control points, strictly increasing.
+ * @param y The Y component of the control points.
+ * @return
+ *
+ * @throws IllegalArgumentException if the X or Y arrays are null, have
+ * different lengths or have fewer than 2 values.
+ * @throws IllegalArgumentException if the X components of the control points are not strictly
+ * increasing.
*/
- public float interpolate(float x) {
- // Handle the boundary cases.
- final int n = mX.length;
- if (Float.isNaN(x)) {
- return x;
- }
- if (x <= mX[0]) {
- return mY[0];
- }
- if (x >= mX[n - 1]) {
- return mY[n - 1];
- }
-
- // Find the index 'i' of the last point with smaller X.
- // We know this will be within the spline due to the boundary tests.
- int i = 0;
- while (x >= mX[i + 1]) {
- i += 1;
- if (x == mX[i]) {
- return mY[i];
- }
- }
-
- // Perform cubic Hermite spline interpolation.
- float h = mX[i + 1] - mX[i];
- float t = (x - mX[i]) / h;
- return (mY[i] * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t)
- + (mY[i + 1] * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t;
+ public static Spline createLinearSpline(float[] x, float[] y) {
+ return new LinearSpline(x, y);
}
- // For debugging.
- @Override
- public String toString() {
- StringBuilder str = new StringBuilder();
- final int n = mX.length;
- str.append("[");
- for (int i = 0; i < n; i++) {
- if (i != 0) {
- str.append(", ");
- }
- str.append("(").append(mX[i]);
- str.append(", ").append(mY[i]);
- str.append(": ").append(mM[i]).append(")");
+ private static boolean isStrictlyIncreasing(float[] x) {
+ if (x == null || x.length < 2) {
+ throw new IllegalArgumentException("There must be at least two control points.");
}
- str.append("]");
- return str.toString();
+ float prev = x[0];
+ for (int i = 1; i < x.length; i++) {
+ float curr = x[i];
+ if (curr <= prev) {
+ return false;
+ }
+ prev = curr;
+ }
+ return true;
+ }
+
+ private static boolean isMonotonic(float[] x) {
+ if (x == null || x.length < 2) {
+ throw new IllegalArgumentException("There must be at least two control points.");
+ }
+ float prev = x[0];
+ for (int i = 1; i < x.length; i++) {
+ float curr = x[i];
+ if (curr < prev) {
+ return false;
+ }
+ prev = curr;
+ }
+ return true;
+ }
+
+ public static class MonotoneCubicSpline extends Spline {
+ private float[] mX;
+ private float[] mY;
+ private float[] mM;
+
+ public MonotoneCubicSpline(float[] x, float[] y) {
+ if (x == null || y == null || x.length != y.length || x.length < 2) {
+ throw new IllegalArgumentException("There must be at least two control "
+ + "points and the arrays must be of equal length.");
+ }
+
+ final int n = x.length;
+ float[] d = new float[n - 1]; // could optimize this out
+ float[] m = new float[n];
+
+ // Compute slopes of secant lines between successive points.
+ for (int i = 0; i < n - 1; i++) {
+ float h = x[i + 1] - x[i];
+ if (h <= 0f) {
+ throw new IllegalArgumentException("The control points must all "
+ + "have strictly increasing X values.");
+ }
+ d[i] = (y[i + 1] - y[i]) / h;
+ }
+
+ // Initialize the tangents as the average of the secants.
+ m[0] = d[0];
+ for (int i = 1; i < n - 1; i++) {
+ m[i] = (d[i - 1] + d[i]) * 0.5f;
+ }
+ m[n - 1] = d[n - 2];
+
+ // Update the tangents to preserve monotonicity.
+ for (int i = 0; i < n - 1; i++) {
+ if (d[i] == 0f) { // successive Y values are equal
+ m[i] = 0f;
+ m[i + 1] = 0f;
+ } else {
+ float a = m[i] / d[i];
+ float b = m[i + 1] / d[i];
+ if (a < 0f || b < 0f) {
+ throw new IllegalArgumentException("The control points must have "
+ + "monotonic Y values.");
+ }
+ float h = FloatMath.hypot(a, b);
+ if (h > 9f) {
+ float t = 3f / h;
+ m[i] = t * a * d[i];
+ m[i + 1] = t * b * d[i];
+ }
+ }
+ }
+
+ mX = x;
+ mY = y;
+ mM = m;
+ }
+
+ @Override
+ public float interpolate(float x) {
+ // Handle the boundary cases.
+ final int n = mX.length;
+ if (Float.isNaN(x)) {
+ return x;
+ }
+ if (x <= mX[0]) {
+ return mY[0];
+ }
+ if (x >= mX[n - 1]) {
+ return mY[n - 1];
+ }
+
+ // Find the index 'i' of the last point with smaller X.
+ // We know this will be within the spline due to the boundary tests.
+ int i = 0;
+ while (x >= mX[i + 1]) {
+ i += 1;
+ if (x == mX[i]) {
+ return mY[i];
+ }
+ }
+
+ // Perform cubic Hermite spline interpolation.
+ float h = mX[i + 1] - mX[i];
+ float t = (x - mX[i]) / h;
+ return (mY[i] * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t)
+ + (mY[i + 1] * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t;
+ }
+
+ // For debugging.
+ @Override
+ public String toString() {
+ StringBuilder str = new StringBuilder();
+ final int n = mX.length;
+ str.append("MonotoneCubicSpline{[");
+ for (int i = 0; i < n; i++) {
+ if (i != 0) {
+ str.append(", ");
+ }
+ str.append("(").append(mX[i]);
+ str.append(", ").append(mY[i]);
+ str.append(": ").append(mM[i]).append(")");
+ }
+ str.append("]}");
+ return str.toString();
+ }
+ }
+
+ public static class LinearSpline extends Spline {
+ private final float[] mX;
+ private final float[] mY;
+ private final float[] mM;
+
+ public LinearSpline(float[] x, float[] y) {
+ if (x == null || y == null || x.length != y.length || x.length < 2) {
+ throw new IllegalArgumentException("There must be at least two control "
+ + "points and the arrays must be of equal length.");
+ }
+ final int N = x.length;
+ mM = new float[N-1];
+ for (int i = 0; i < N-1; i++) {
+ mM[i] = (y[i+1] - y[i]) / (x[i+1] - x[i]);
+ }
+ mX = x;
+ mY = y;
+ }
+
+ @Override
+ public float interpolate(float x) {
+ // Handle the boundary cases.
+ final int n = mX.length;
+ if (Float.isNaN(x)) {
+ return x;
+ }
+ if (x <= mX[0]) {
+ return mY[0];
+ }
+ if (x >= mX[n - 1]) {
+ return mY[n - 1];
+ }
+
+ // Find the index 'i' of the last point with smaller X.
+ // We know this will be within the spline due to the boundary tests.
+ int i = 0;
+ while (x >= mX[i + 1]) {
+ i += 1;
+ if (x == mX[i]) {
+ return mY[i];
+ }
+ }
+ return mY[i] + mM[i] * (x - mX[i]);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder str = new StringBuilder();
+ final int n = mX.length;
+ str.append("LinearSpline{[");
+ for (int i = 0; i < n; i++) {
+ if (i != 0) {
+ str.append(", ");
+ }
+ str.append("(").append(mX[i]);
+ str.append(", ").append(mY[i]);
+ if (i < n-1) {
+ str.append(": ").append(mM[i]);
+ }
+ str.append(")");
+ }
+ str.append("]}");
+ return str.toString();
+ }
}
}
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 413ea04..fa4a13a 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -87,8 +87,11 @@
private float mFinalValue;
private TimeInterpolator mInterpolator;
- private boolean mStarted = false;
- private boolean mFinished = false;
+ private static final int STATE_PREPARE = 0;
+ private static final int STATE_DELAYED = 1;
+ private static final int STATE_RUNNING = 2;
+ private static final int STATE_FINISHED = 3;
+ private int mState = STATE_PREPARE;
private long mUnscaledDuration = 300;
private long mUnscaledStartDelay = 0;
@@ -142,7 +145,7 @@
}
private void checkMutable() {
- if (mStarted) {
+ if (mState != STATE_PREPARE) {
throw new IllegalStateException("Animator has already started, cannot change it now!");
}
}
@@ -170,11 +173,11 @@
throw new IllegalStateException("Missing target!");
}
- if (mStarted) {
+ if (mState != STATE_PREPARE) {
throw new IllegalStateException("Already started!");
}
- mStarted = true;
+ mState = STATE_DELAYED;
applyInterpolator();
if (mStartDelay <= 0 || !mUiThreadHandlesDelay) {
@@ -186,6 +189,7 @@
}
private void doStart() {
+ mState = STATE_RUNNING;
nStart(mNativePtr.get(), this);
// Alpha is a special snowflake that has the canonical value stored
@@ -197,11 +201,7 @@
mViewTarget.mTransformationInfo.mAlpha = mFinalValue;
}
- final ArrayList<AnimatorListener> listeners = cloneListeners();
- final int numListeners = listeners == null ? 0 : listeners.size();
- for (int i = 0; i < numListeners; i++) {
- listeners.get(i).onAnimationStart(this);
- }
+ notifyStartListeners();
if (mViewTarget != null) {
// Kick off a frame to start the process
@@ -209,10 +209,21 @@
}
}
+ private void notifyStartListeners() {
+ final ArrayList<AnimatorListener> listeners = cloneListeners();
+ final int numListeners = listeners == null ? 0 : listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ listeners.get(i).onAnimationStart(this);
+ }
+ }
+
@Override
public void cancel() {
- if (!mFinished) {
- getHelper().removeDelayedAnimation(this);
+ if (mState != STATE_FINISHED) {
+ if (mState == STATE_DELAYED) {
+ getHelper().removeDelayedAnimation(this);
+ notifyStartListeners();
+ }
nEnd(mNativePtr.get());
final ArrayList<AnimatorListener> listeners = cloneListeners();
@@ -220,12 +231,17 @@
for (int i = 0; i < numListeners; i++) {
listeners.get(i).onAnimationCancel(this);
}
+
+ if (mViewTarget != null) {
+ // Kick off a frame to flush the state change
+ mViewTarget.invalidateViewProperty(true, false);
+ }
}
}
@Override
public void end() {
- if (!mFinished) {
+ if (mState != STATE_FINISHED) {
nEnd(mNativePtr.get());
}
}
@@ -299,12 +315,12 @@
@Override
public boolean isRunning() {
- return mStarted && !mFinished;
+ return mState == STATE_DELAYED || mState == STATE_RUNNING;
}
@Override
public boolean isStarted() {
- return mStarted;
+ return mState != STATE_PREPARE;
}
@Override
@@ -319,7 +335,11 @@
}
protected void onFinished() {
- mFinished = true;
+ if (mState == STATE_DELAYED) {
+ getHelper().removeDelayedAnimation(this);
+ notifyStartListeners();
+ }
+ mState = STATE_FINISHED;
final ArrayList<AnimatorListener> listeners = cloneListeners();
final int numListeners = listeners == null ? 0 : listeners.size();
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 602f955..56bdb9b 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -176,7 +176,7 @@
RemoteViewsAdapter adapter;
final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
if ((adapter = mAdapter.get()) != null) {
- mgr.unbindRemoteViewsService(context.getPackageName(), appWidgetId, intent);
+ mgr.unbindRemoteViewsService(context.getOpPackageName(), appWidgetId, intent);
} else {
Slog.w(TAG, "unbind: adapter was null");
}
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 818efaa..ece8aa4 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -244,6 +244,16 @@
// Set the default context, since setPopupTheme() may be a no-op.
mPopupContext = mContext;
setPopupTheme(a.getResourceId(R.styleable.Toolbar_popupTheme, 0));
+
+ final Drawable navIcon = a.getDrawable(R.styleable.Toolbar_navigationIcon);
+ if (navIcon != null) {
+ setNavigationIcon(navIcon);
+ final CharSequence navDesc = a.getText(
+ R.styleable.Toolbar_navigationContentDescription);
+ if (!TextUtils.isEmpty(navDesc)) {
+ setNavigationContentDescription(navDesc);
+ }
+ }
a.recycle();
}
@@ -669,6 +679,8 @@
* as screen readers or tooltips.
*
* @return The navigation button's content description
+ *
+ * @attr ref android.R.styleable#Toolbar_navigationContentDescription
*/
@Nullable
public CharSequence getNavigationContentDescription() {
@@ -682,6 +694,8 @@
*
* @param resId Resource ID of a content description string to set, or 0 to
* clear the description
+ *
+ * @attr ref android.R.styleable#Toolbar_navigationContentDescription
*/
public void setNavigationContentDescription(int resId) {
setNavigationContentDescription(resId != 0 ? getContext().getText(resId) : null);
@@ -694,6 +708,8 @@
*
* @param description Content description to set, or <code>null</code> to
* clear the content description
+ *
+ * @attr ref android.R.styleable#Toolbar_navigationContentDescription
*/
public void setNavigationContentDescription(@Nullable CharSequence description) {
if (!TextUtils.isEmpty(description)) {
@@ -715,6 +731,8 @@
* tooltips.</p>
*
* @param resId Resource ID of a drawable to set
+ *
+ * @attr ref android.R.styleable#Toolbar_navigationIcon
*/
public void setNavigationIcon(int resId) {
setNavigationIcon(getContext().getDrawable(resId));
@@ -731,6 +749,8 @@
* tooltips.</p>
*
* @param icon Drawable to set, may be null to clear the icon
+ *
+ * @attr ref android.R.styleable#Toolbar_navigationIcon
*/
public void setNavigationIcon(@Nullable Drawable icon) {
if (icon != null) {
@@ -751,6 +771,8 @@
* Return the current drawable used as the navigation icon.
*
* @return The navigation icon drawable
+ *
+ * @attr ref android.R.styleable#Toolbar_navigationIcon
*/
@Nullable
public Drawable getNavigationIcon() {
@@ -1316,6 +1338,8 @@
final View bottomChild = layoutSubtitle ? mSubtitleTextView : mTitleTextView;
final LayoutParams toplp = (LayoutParams) topChild.getLayoutParams();
final LayoutParams bottomlp = (LayoutParams) bottomChild.getLayoutParams();
+ final boolean titleHasWidth = layoutTitle && mTitleTextView.getMeasuredWidth() > 0
+ || layoutSubtitle && mSubtitleTextView.getMeasuredWidth() > 0;
switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) {
case Gravity.TOP:
@@ -1343,7 +1367,7 @@
break;
}
if (isRtl) {
- final int rd = mTitleMarginStart - collapsingMargins[1];
+ final int rd = (titleHasWidth ? mTitleMarginStart : 0) - collapsingMargins[1];
right -= Math.max(0, rd);
collapsingMargins[1] = Math.max(0, -rd);
int titleRight = right;
@@ -1366,9 +1390,11 @@
subtitleRight = subtitleRight - mTitleMarginEnd;
titleTop = subtitleBottom + lp.bottomMargin;
}
- right = Math.min(titleRight, subtitleRight);
+ if (titleHasWidth) {
+ right = Math.min(titleRight, subtitleRight);
+ }
} else {
- final int ld = mTitleMarginStart - collapsingMargins[0];
+ final int ld = (titleHasWidth ? mTitleMarginStart : 0) - collapsingMargins[0];
left += Math.max(0, ld);
collapsingMargins[0] = Math.max(0, -ld);
int titleLeft = left;
@@ -1391,7 +1417,9 @@
subtitleLeft = subtitleRight + mTitleMarginEnd;
titleTop = subtitleBottom + lp.bottomMargin;
}
- left = Math.max(titleLeft, subtitleLeft);
+ if (titleHasWidth) {
+ left = Math.max(titleLeft, subtitleLeft);
+ }
}
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 61b4567..b6e7353 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -23,7 +23,6 @@
import android.os.AsyncTask;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.Slog;
import android.widget.AbsListView;
import android.widget.GridView;
@@ -73,6 +72,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -100,7 +100,7 @@
private boolean mResolvingHome = false;
private UsageStatsManager mUsm;
- private ArrayMap<String, UsageStats> mStats;
+ private Map<String, UsageStats> mStats;
private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
private boolean mRegistered;
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 2967938..ba236f3 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -191,6 +191,20 @@
public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) {
if (DEBUG) Log.v(TAG, "requestUpdateCursorAnchorInfo " + cursorUpdateMode);
+ // It is possible that any other bit is used as a valid flag in a future release.
+ // We should reject the entire request in such a case.
+ final int KNOWN_FLAGS_MASK = InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE |
+ InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_MONITOR;
+ final int unknownFlags = cursorUpdateMode & ~KNOWN_FLAGS_MASK;
+ if (unknownFlags != 0) {
+ if (DEBUG) {
+ Log.d(TAG, "Rejecting requestUpdateCursorAnchorInfo due to unknown flags." +
+ " cursorUpdateMode=" + cursorUpdateMode +
+ " unknownFlags=" + unknownFlags);
+ }
+ return false;
+ }
+
if (mIMM == null) {
// In this case, TYPE_CURSOR_ANCHOR_INFO is not handled.
// TODO: Return some notification code rather than false to indicate method that
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index 3595033..ef916ed1 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -53,8 +53,8 @@
android:visibility="gone"
/>
<ImageView android:id="@+id/profile_badge_large_template"
- android:layout_width="20dp"
- android:layout_height="20dp"
+ android:layout_width="@dimen/notification_badge_size"
+ android:layout_height="@dimen/notification_badge_size"
android:layout_weight="0"
android:layout_marginStart="4dp"
android:scaleType="fitCenter"
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index d601d4a3..3415814 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -39,11 +39,12 @@
<include layout="@layout/notification_template_part_line2" />
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="0dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="10dp"
android:orientation="horizontal"
android:gravity="top"
+ android:layout_weight="1"
>
<TextView android:id="@+id/big_text"
android:textAppearance="@style/TextAppearance.Material.Notification"
@@ -54,8 +55,8 @@
android:visibility="gone"
/>
<ImageView android:id="@+id/profile_badge_large_template"
- android:layout_width="20dp"
- android:layout_height="20dp"
+ android:layout_width="@dimen/notification_badge_size"
+ android:layout_height="@dimen/notification_badge_size"
android:layout_weight="0"
android:layout_marginStart="4dp"
android:scaleType="fitCenter"
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index 2bd9f81..2382d18 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -37,95 +37,27 @@
>
<include layout="@layout/notification_template_part_line1" />
<include layout="@layout/notification_template_part_line2" />
+
+ <!-- We can't have another vertical linear layout here with weight != 0 so this forces us to
+ put the badge on the first line. -->
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_height="0dp"
android:orientation="horizontal"
- android:gravity="top"
>
- <LinearLayout
+ <TextView android:id="@+id/inbox_text0"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
android:layout_width="0dp"
- android:layout_weight="1"
android:layout_height="wrap_content"
- android:orientation="vertical"
- >
- <TextView android:id="@+id/inbox_text0"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text1"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text2"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text3"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text4"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text5"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text6"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_more"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- android:text="@android:string/ellipsis"
- />
- </LinearLayout>
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
<ImageView android:id="@+id/profile_badge_large_template"
- android:layout_width="20dp"
- android:layout_height="20dp"
+ android:layout_width="@dimen/notification_badge_size"
+ android:layout_height="@dimen/notification_badge_size"
android:layout_weight="0"
android:layout_marginStart="4dp"
android:layout_marginEnd="8dp"
@@ -133,6 +65,77 @@
android:visibility="gone"
/>
</LinearLayout>
+ <TextView android:id="@+id/inbox_text1"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text2"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text3"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text4"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text5"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text6"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_more"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ android:text="@android:string/ellipsis"
+ />
<FrameLayout
android:id="@+id/inbox_end_pad"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/notification_template_part_line2.xml b/core/res/res/layout/notification_template_part_line2.xml
index 28d2bef..7e99c5e 100644
--- a/core/res/res/layout/notification_template_part_line2.xml
+++ b/core/res/res/layout/notification_template_part_line2.xml
@@ -20,8 +20,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
- android:visibility="gone"
- android:layout_weight="0"
android:orientation="horizontal"
android:gravity="center_vertical"
>
@@ -39,8 +37,8 @@
android:layout_weight="1"
/>
<ImageView android:id="@+id/profile_badge_line2"
- android:layout_width="20dp"
- android:layout_height="20dp"
+ android:layout_width="@dimen/notification_badge_size"
+ android:layout_height="@dimen/notification_badge_size"
android:layout_weight="0"
android:layout_marginStart="4dp"
android:scaleType="fitCenter"
diff --git a/core/res/res/layout/notification_template_part_line3.xml b/core/res/res/layout/notification_template_part_line3.xml
index 56de5c9..6c043a0 100644
--- a/core/res/res/layout/notification_template_part_line3.xml
+++ b/core/res/res/layout/notification_template_part_line3.xml
@@ -44,8 +44,8 @@
android:paddingStart="8dp"
/>
<ImageView android:id="@+id/profile_badge_line3"
- android:layout_width="20dp"
- android:layout_height="20dp"
+ android:layout_width="@dimen/notification_badge_size"
+ android:layout_height="@dimen/notification_badge_size"
android:layout_gravity="center"
android:layout_weight="0"
android:layout_marginStart="4dp"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9da116a..a798d2e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7318,6 +7318,12 @@
<!-- Reference to a theme that should be used to inflate popups
shown by widgets in the toolbar. -->
<attr name="popupTheme" format="reference" />
+ <!-- Icon drawable to use for the navigation button located at
+ the start of the toolbar. -->
+ <attr name="navigationIcon" format="reference" />
+ <!-- Text to set as the content description for the navigation button
+ located at the start of the toolbar. -->
+ <attr name="navigationContentDescription" format="string" />
</declare-styleable>
<declare-styleable name="Toolbar_LayoutParams">
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 4e3abb9..77b451f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -236,6 +236,9 @@
<!-- Padding for notification icon when drawn with circle around it -->
<dimen name="notification_large_icon_circle_padding">11dp</dimen>
+ <!-- Size of the profile badge for notifications -->
+ <dimen name="notification_badge_size">16dp</dimen>
+
<!-- Keyguard dimensions -->
<!-- TEMP -->
<dimen name="kg_security_panel_height">600dp</dimen>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 4c59f73..bd24f3e 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -87,4 +87,6 @@
<item type="id" name="transitionPosition" />
<item type="id" name="transitionTransform" />
<item type="id" name="parentMatrix" />
+ <item type="id" name="statusBarBackground" />
+ <item type="id" name="navigationBarBackground" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3b49bed..5e76a87 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2280,12 +2280,16 @@
<public type="attr" name="reparentWithOverlay" />
<public type="attr" name="ambientShadowAlpha" />
<public type="attr" name="spotShadowAlpha" />
+ <public type="attr" name="navigationIcon" />
+ <public type="attr" name="navigationContentDescription" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
<public-padding type="id" name="l_resource_pad" end="0x01020040" />
<public type="id" name="mask" />
+ <public type="id" name="statusBarBackground" />
+ <public type="id" name="navigationBarBackground" />
<public-padding type="style" name="l_resource_pad" end="0x01030200" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d8c29b0..622a01a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -357,6 +357,7 @@
<java-symbol type="dimen" name="notification_top_pad_large_text" />
<java-symbol type="dimen" name="notification_top_pad_large_text_narrow" />
<java-symbol type="dimen" name="notification_large_icon_circle_padding" />
+ <java-symbol type="dimen" name="notification_badge_size" />
<java-symbol type="dimen" name="immersive_mode_cling_width" />
<java-symbol type="dimen" name="circular_display_mask_offset" />
diff --git a/docs/html/guide/topics/admin/device-admin.jd b/docs/html/guide/topics/admin/device-admin.jd
index ee6b814..bed4b4d 100644
--- a/docs/html/guide/topics/admin/device-admin.jd
+++ b/docs/html/guide/topics/admin/device-admin.jd
@@ -28,12 +28,6 @@
<li>{@link android.app.admin.DevicePolicyManager}</li>
<li>{@link android.app.admin.DeviceAdminInfo}</li>
</ol>
- <h2>Related samples</h2>
- <ol>
- <li><a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html">
-DeviceAdminSample</a></li>
-</ol>
</div>
</div>
@@ -232,18 +226,12 @@
<h2 id="sample">Sample Application</h2>
-<p>The examples used in this document are based on the <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html">
-Device Administration API
-sample</a>, which is included in the SDK samples. For information on downloading and
-installing the SDK samples, see <a
-href="{@docRoot}resources/samples/get.html">
-Getting the Samples</a>. Here is the <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html">
-complete code</a> for
-the sample. </p>
-<p>The
-sample application offers a demo of device admin features. It presents users
+<p>The examples used in this document are based on the Device Administration API
+sample, which is included in the SDK samples (available through the
+Android SDK Manager) and located on your system as
+<code><sdk_root>/ApiDemos/app/src/main/java/com/example/android/apis/app/DeviceAdminSample.java</code>.</p>
+
+<p>The sample application offers a demo of device admin features. It presents users
with a user interface that lets them enable the device admin application. Once
they've enabled the application, they can use the buttons in the user interface
to do the following:</p>
@@ -676,7 +664,8 @@
<p>You can also programmatically tell the device to lock immediately:</p>
<pre>
DevicePolicyManager mDPM;
-mDPM.lockNow();</pre>
+mDPM.lockNow();
+</pre>
@@ -692,12 +681,12 @@
<pre>
DevicePolicyManager mDPM;
mDPM.wipeData(0);</pre>
-<p>The {@link android.app.admin.DevicePolicyManager#wipeData wipeData()} method takes as its parameter a bit mask of
-additional options. Currently the value must be 0. </p>
+<p>The {@link android.app.admin.DevicePolicyManager#wipeData wipeData()} method takes as its
+ parameter a bit mask of additional options. Currently the value must be 0. </p>
<h4>Disable camera</h4>
<p>Beginning with Android 4.0, you can disable the camera. Note that this doesn't have to be a permanent disabling. The camera can be enabled/disabled dynamically based on context, time, and so on. </p>
-<p>You control whether the camera is disabled by using the
+<p>You control whether the camera is disabled by using the
{@link android.app.admin.DevicePolicyManager#setCameraDisabled(android.content.ComponentName, boolean) setCameraDisabled()} method. For example, this snippet sets the camera to be enabled or disabled based on a checkbox setting:</p>
<pre>private CheckBoxPreference mDisableCameraCheckbox;
@@ -708,8 +697,8 @@
</pre>
-<h4 id=storage">Storage encryption</h4>
-<p>Beginning with Android 3.0, you can use the
+<h4 id="storage">Storage encryption</h4>
+<p>Beginning with Android 3.0, you can use the
{@link android.app.admin.DevicePolicyManager#setStorageEncryption(android.content.ComponentName,boolean) setStorageEncryption()}
method to set a policy requiring encryption of the storage area, where supported.</p>
@@ -722,5 +711,5 @@
mDPM.setStorageEncryption(mDeviceAdminSample, true);
</pre>
<p>
-See the <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html"> Device Administration API sample</a> for a complete
-example of how to enable storage encryption.</p>
+See the Device Administration API sample for a complete example of how to enable storage encryption.
+</p>
\ No newline at end of file
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 11568d2..c65efe4 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1557,7 +1557,7 @@
};
const char16_t* valueToString(const Res_value* value, size_t stringBlock,
char16_t tmpBuffer[TMP_BUFFER_SIZE],
- size_t* outLen);
+ size_t* outLen) const;
struct bag_entry {
ssize_t stringBlock;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 3f014ef..690b1d6 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2939,7 +2939,7 @@
for (size_t i = 0; i < bags->size(); i++) {
TABLE_NOISY(printf("type=%d\n", i));
const TypeList& typeList = types[i];
- if (typeList.isEmpty()) {
+ if (!typeList.isEmpty()) {
bag_set** typeBags = bags->get(i);
TABLE_NOISY(printf("typeBags=%p\n", typeBags));
if (typeBags) {
@@ -3728,7 +3728,7 @@
const char16_t* ResTable::valueToString(
const Res_value* value, size_t stringBlock,
- char16_t /*tmpBuffer*/ [TMP_BUFFER_SIZE], size_t* outLen)
+ char16_t /*tmpBuffer*/ [TMP_BUFFER_SIZE], size_t* outLen) const
{
if (!value) {
return NULL;
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index a10c387..5808d20 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -26,6 +26,7 @@
Idmap_test.cpp \
ResTable_test.cpp \
Split_test.cpp \
+ Theme_test.cpp \
TypeWrappers_test.cpp \
ZipUtils_test.cpp
diff --git a/libs/androidfw/tests/BackupData_test.cpp b/libs/androidfw/tests/BackupData_test.cpp
index 17f91ca..92af7fe 100644
--- a/libs/androidfw/tests/BackupData_test.cpp
+++ b/libs/androidfw/tests/BackupData_test.cpp
@@ -45,7 +45,7 @@
class BackupDataTest : public testing::Test {
protected:
char* m_external_storage;
- char* m_filename;
+ String8 mFilename;
String8 mKey1;
String8 mKey2;
String8 mKey3;
@@ -53,15 +53,13 @@
virtual void SetUp() {
m_external_storage = getenv("EXTERNAL_STORAGE");
+ mFilename.append(m_external_storage);
+ mFilename.append(TEST_FILENAME);
- const int totalLen = strlen(m_external_storage) + strlen(TEST_FILENAME) + 1;
- m_filename = new char[totalLen];
- snprintf(m_filename, totalLen, "%s%s", m_external_storage, TEST_FILENAME);
-
- ::unlink(m_filename);
- int fd = ::open(m_filename, O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ ::unlink(mFilename.string());
+ int fd = ::open(mFilename.string(), O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd < 0) {
- FAIL() << "Couldn't create " << m_filename << " for writing";
+ FAIL() << "Couldn't create " << mFilename.string() << " for writing";
}
mKey1 = String8(KEY1);
mKey2 = String8(KEY2);
@@ -74,7 +72,7 @@
};
TEST_F(BackupDataTest, WriteAndReadSingle) {
- int fd = ::open(m_filename, O_WRONLY);
+ int fd = ::open(mFilename.string(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
EXPECT_EQ(NO_ERROR, writer->WriteEntityHeader(mKey1, sizeof(DATA1)))
@@ -83,7 +81,7 @@
<< "WriteEntityData returned an error";
::close(fd);
- fd = ::open(m_filename, O_RDONLY);
+ fd = ::open(mFilename.string(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
EXPECT_EQ(NO_ERROR, reader->Status())
<< "Reader ctor failed";
@@ -116,7 +114,7 @@
}
TEST_F(BackupDataTest, WriteAndReadMultiple) {
- int fd = ::open(m_filename, O_WRONLY);
+ int fd = ::open(mFilename.string(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, sizeof(DATA1));
writer->WriteEntityData(DATA1, sizeof(DATA1));
@@ -124,7 +122,7 @@
writer->WriteEntityData(DATA2, sizeof(DATA2));
::close(fd);
- fd = ::open(m_filename, O_RDONLY);
+ fd = ::open(mFilename.string(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -164,7 +162,7 @@
}
TEST_F(BackupDataTest, SkipEntity) {
- int fd = ::open(m_filename, O_WRONLY);
+ int fd = ::open(mFilename.string(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, sizeof(DATA1));
writer->WriteEntityData(DATA1, sizeof(DATA1));
@@ -174,7 +172,7 @@
writer->WriteEntityData(DATA3, sizeof(DATA3));
::close(fd);
- fd = ::open(m_filename, O_RDONLY);
+ fd = ::open(mFilename.string(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -219,14 +217,14 @@
}
TEST_F(BackupDataTest, DeleteEntity) {
- int fd = ::open(m_filename, O_WRONLY);
+ int fd = ::open(mFilename.string(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, sizeof(DATA1));
writer->WriteEntityData(DATA1, sizeof(DATA1));
writer->WriteEntityHeader(mKey2, -1);
::close(fd);
- fd = ::open(m_filename, O_RDONLY);
+ fd = ::open(mFilename.string(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -258,7 +256,7 @@
}
TEST_F(BackupDataTest, EneityAfterDelete) {
- int fd = ::open(m_filename, O_WRONLY);
+ int fd = ::open(mFilename.string(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, sizeof(DATA1));
writer->WriteEntityData(DATA1, sizeof(DATA1));
@@ -267,7 +265,7 @@
writer->WriteEntityData(DATA3, sizeof(DATA3));
::close(fd);
- fd = ::open(m_filename, O_RDONLY);
+ fd = ::open(mFilename.string(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -319,7 +317,7 @@
}
TEST_F(BackupDataTest, OnlyDeleteEntities) {
- int fd = ::open(m_filename, O_WRONLY);
+ int fd = ::open(mFilename.string(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, -1);
writer->WriteEntityHeader(mKey2, -1);
@@ -327,7 +325,7 @@
writer->WriteEntityHeader(mKey4, -1);
::close(fd);
- fd = ::open(m_filename, O_RDONLY);
+ fd = ::open(mFilename.string(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -387,13 +385,13 @@
}
TEST_F(BackupDataTest, ReadDeletedEntityData) {
- int fd = ::open(m_filename, O_WRONLY);
+ int fd = ::open(mFilename.string(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, -1);
writer->WriteEntityHeader(mKey2, -1);
::close(fd);
- fd = ::open(m_filename, O_RDONLY);
+ fd = ::open(mFilename.string(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -431,6 +429,7 @@
EXPECT_EQ(-1, (int) dataSize)
<< "not recognizing deletion on second entity";
+ delete[] dataBytes;
delete writer;
delete reader;
}
diff --git a/libs/androidfw/tests/ObbFile_test.cpp b/libs/androidfw/tests/ObbFile_test.cpp
index 7a4dd13..1151121 100644
--- a/libs/androidfw/tests/ObbFile_test.cpp
+++ b/libs/androidfw/tests/ObbFile_test.cpp
@@ -34,20 +34,18 @@
class ObbFileTest : public testing::Test {
protected:
sp<ObbFile> mObbFile;
- char* mExternalStorage;
- char* mFileName;
+ String8 mFileName;
virtual void SetUp() {
mObbFile = new ObbFile();
- mExternalStorage = getenv("EXTERNAL_STORAGE");
+ char* externalStorage = getenv("EXTERNAL_STORAGE");
- const int totalLen = strlen(mExternalStorage) + strlen(TEST_FILENAME) + 1;
- mFileName = new char[totalLen];
- snprintf(mFileName, totalLen, "%s%s", mExternalStorage, TEST_FILENAME);
+ mFileName.append(externalStorage);
+ mFileName.append(TEST_FILENAME);
- int fd = ::open(mFileName, O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ int fd = ::open(mFileName.string(), O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd < 0) {
- FAIL() << "Couldn't create " << mFileName << " for tests";
+ FAIL() << "Couldn't create " << mFileName.string() << " for tests";
}
}
@@ -71,12 +69,12 @@
EXPECT_TRUE(mObbFile->setSalt(salt, SALT_SIZE))
<< "Salt should be successfully set";
- EXPECT_TRUE(mObbFile->writeTo(mFileName))
+ EXPECT_TRUE(mObbFile->writeTo(mFileName.string()))
<< "couldn't write to fake .obb file";
mObbFile = new ObbFile();
- EXPECT_TRUE(mObbFile->readFrom(mFileName))
+ EXPECT_TRUE(mObbFile->readFrom(mFileName.string()))
<< "couldn't read from fake .obb file";
EXPECT_EQ(versionNum, mObbFile->getVersion())
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
new file mode 100644
index 0000000..4d07130
--- /dev/null
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <androidfw/ResourceTypes.h>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include "TestHelpers.h"
+#include "data/system/R.h"
+#include "data/app/R.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace {
+
+#include "data/system/system_arsc.h"
+#include "data/app/app_arsc.h"
+
+enum { MAY_NOT_BE_BAG = false };
+
+/**
+ * TODO(adamlesinski): Enable when fixed.
+ */
+TEST(ThemeTest, DISABLED_shouldCopyThemeFromDifferentResTable) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(system_arsc, system_arsc_len));
+ ASSERT_EQ(NO_ERROR, table.add(app_arsc, app_arsc_len));
+
+ ResTable::Theme theme1(table);
+ ASSERT_EQ(NO_ERROR, theme1.applyStyle(app::R::style::Theme_One));
+ Res_value val;
+ ASSERT_GE(theme1.getAttribute(android::R::attr::background, &val), 0);
+ ASSERT_EQ(Res_value::TYPE_INT_COLOR_RGB8, val.dataType);
+ ASSERT_EQ(uint32_t(0xffff0000), val.data);
+ ASSERT_GE(theme1.getAttribute(app::R::attr::number, &val), 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(1), val.data);
+
+ ResTable table2;
+ ASSERT_EQ(NO_ERROR, table2.add(system_arsc, system_arsc_len));
+ ASSERT_EQ(NO_ERROR, table2.add(app_arsc, app_arsc_len));
+
+ ResTable::Theme theme2(table2);
+ ASSERT_EQ(NO_ERROR, theme2.setTo(theme1));
+ ASSERT_GE(theme2.getAttribute(android::R::attr::background, &val), 0);
+ ASSERT_EQ(Res_value::TYPE_INT_COLOR_RGB8, val.dataType);
+ ASSERT_EQ(uint32_t(0xffff0000), val.data);
+ ASSERT_GE(theme2.getAttribute(app::R::attr::number, &val), 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(1), val.data);
+}
+
+}
diff --git a/libs/androidfw/tests/data/app/AndroidManifest.xml b/libs/androidfw/tests/data/app/AndroidManifest.xml
new file mode 100644
index 0000000..bfa3a39
--- /dev/null
+++ b/libs/androidfw/tests/data/app/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.app">
+</manifest>
diff --git a/libs/androidfw/tests/data/app/R.h b/libs/androidfw/tests/data/app/R.h
new file mode 100644
index 0000000..780a116
--- /dev/null
+++ b/libs/androidfw/tests/data/app/R.h
@@ -0,0 +1,22 @@
+#ifndef __APP_R_H
+#define __APP_R_H
+
+namespace app {
+namespace R {
+
+namespace attr {
+ enum {
+ number = 0x7f010000, // default
+ };
+}
+
+namespace style {
+ enum {
+ Theme_One = 0x7f020000, // default
+ };
+}
+
+} // namespace R
+} // namespace app
+
+#endif // __APP_R_H
diff --git a/libs/androidfw/tests/data/app/app_arsc.h b/libs/androidfw/tests/data/app/app_arsc.h
new file mode 100644
index 0000000..d5d9a3b
--- /dev/null
+++ b/libs/androidfw/tests/data/app/app_arsc.h
@@ -0,0 +1,62 @@
+unsigned char app_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0xc4, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x9c, 0x02, 0x00, 0x00,
+ 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+ 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+ 0x64, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
+ 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00,
+ 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+ 0x4c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6e, 0x00,
+ 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
+ 0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+ 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+ 0x64, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00
+};
+unsigned int app_arsc_len = 708;
diff --git a/libs/androidfw/tests/data/app/build b/libs/androidfw/tests/data/app/build
new file mode 100755
index 0000000..89c4641
--- /dev/null
+++ b/libs/androidfw/tests/data/app/build
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+aapt package -v -I ../system/bundle.apk -M AndroidManifest.xml -S res -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc app.arsc && \
+xxd -i app.arsc > app_arsc.h
diff --git a/libs/androidfw/tests/data/app/res/values/values.xml b/libs/androidfw/tests/data/app/res/values/values.xml
new file mode 100644
index 0000000..b0ead38
--- /dev/null
+++ b/libs/androidfw/tests/data/app/res/values/values.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <attr name="number" format="integer"/>
+ <style name="Theme.One" parent="@android:style/Theme.One">
+ <item name="number">1</item>
+ </style>
+</resources>
diff --git a/libs/androidfw/tests/data/system/AndroidManifest.xml b/libs/androidfw/tests/data/system/AndroidManifest.xml
new file mode 100644
index 0000000..af105ee
--- /dev/null
+++ b/libs/androidfw/tests/data/system/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+</manifest>
diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h
new file mode 100644
index 0000000..7a9d3db
--- /dev/null
+++ b/libs/androidfw/tests/data/system/R.h
@@ -0,0 +1,23 @@
+#ifndef __ANDROID_R_H
+#define __ANDROID_R_H
+
+namespace android {
+namespace R {
+
+namespace attr {
+ enum {
+ background = 0x01010000, // default
+ foreground = 0x01010001, // default
+ };
+}
+
+namespace style {
+ enum {
+ Theme_One = 0x01020000, // default
+ };
+}
+
+} // namespace R
+} // namespace android
+
+#endif // __ANDROID_R_H
diff --git a/libs/androidfw/tests/data/system/build b/libs/androidfw/tests/data/system/build
new file mode 100755
index 0000000..2a3ac0b
--- /dev/null
+++ b/libs/androidfw/tests/data/system/build
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+aapt package -x -M AndroidManifest.xml -S res -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc system.arsc && \
+xxd -i system.arsc > system_arsc.h
diff --git a/libs/androidfw/tests/data/system/res/values/themes.xml b/libs/androidfw/tests/data/system/res/values/themes.xml
new file mode 100644
index 0000000..b29848e
--- /dev/null
+++ b/libs/androidfw/tests/data/system/res/values/themes.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <public name="background" type="attr" id="0x01010000"/>
+ <public name="foreground" type="attr" id="0x01010001"/>
+ <public name="Theme.One" type="style" id="0x01020000"/>
+
+ <attr name="background" format="color|reference"/>
+ <attr name="foreground" format="color|reference"/>
+ <style name="Theme.One" parent="">
+ <item name="android:background">#ff0000</item>
+ <item name="android:foreground">#000000</item>
+ </style>
+</resources>
diff --git a/libs/androidfw/tests/data/system/system_arsc.h b/libs/androidfw/tests/data/system/system_arsc.h
new file mode 100644
index 0000000..215ecae
--- /dev/null
+++ b/libs/androidfw/tests/data/system/system_arsc.h
@@ -0,0 +1,69 @@
+unsigned char system_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0x18, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xf0, 0x02, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
+ 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
+ 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00,
+ 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+ 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x62, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x67, 0x00,
+ 0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x67, 0x00,
+ 0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
+ 0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40,
+ 0x01, 0x02, 0x44, 0x00, 0x84, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x08, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x44, 0x00,
+ 0x70, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0xff, 0xff,
+ 0x01, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xff
+};
+unsigned int system_arsc_len = 792;
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
index 9cc83ed..7834ef8 100644
--- a/libs/hwui/AmbientShadow.cpp
+++ b/libs/hwui/AmbientShadow.cpp
@@ -16,6 +16,43 @@
#define LOG_TAG "OpenGLRenderer"
+/**
+ * Extra vertices for the corner for smoother corner.
+ * Only for outer vertices.
+ * Note that we use such extra memory to avoid an extra loop.
+ */
+// For half circle, we could add EXTRA_VERTEX_PER_PI vertices.
+// Set to 1 if we don't want to have any.
+#define EXTRA_CORNER_VERTEX_PER_PI 12
+
+// For the whole polygon, the sum of all the deltas b/t normals is 2 * M_PI,
+// therefore, the maximum number of extra vertices will be twice bigger.
+#define MAX_EXTRA_CORNER_VERTEX_NUMBER (2 * EXTRA_CORNER_VERTEX_PER_PI)
+
+// For each RADIANS_DIVISOR, we would allocate one more vertex b/t the normals.
+#define CORNER_RADIANS_DIVISOR (M_PI / EXTRA_CORNER_VERTEX_PER_PI)
+
+/**
+ * Extra vertices for the Edge for interpolation artifacts.
+ * Same value for both inner and outer vertices.
+ */
+#define EXTRA_EDGE_VERTEX_PER_PI 50
+
+#define MAX_EXTRA_EDGE_VERTEX_NUMBER (2 * EXTRA_EDGE_VERTEX_PER_PI)
+
+#define EDGE_RADIANS_DIVISOR (M_PI / EXTRA_EDGE_VERTEX_PER_PI)
+
+/**
+ * Other constants:
+ */
+// For the edge of the penumbra, the opacity is 0.
+#define OUTER_OPACITY (0.0f)
+
+// Once the alpha difference is greater than this threshold, we will allocate extra
+// edge vertices.
+// If this is set to negative value, then all the edge will be tessellated.
+#define ALPHA_THRESHOLD (0.1f / 255.0f)
+
#include <math.h>
#include <utils/Log.h>
#include <utils/Vector.h>
@@ -23,11 +60,97 @@
#include "AmbientShadow.h"
#include "ShadowTessellator.h"
#include "Vertex.h"
+#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
/**
+ * Local utility functions.
+ */
+inline Vector2 getNormalFromVertices(const Vector3* vertices, int current, int next) {
+ // Convert from Vector3 to Vector2 first.
+ Vector2 currentVertex = { vertices[current].x, vertices[current].y };
+ Vector2 nextVertex = { vertices[next].x, vertices[next].y };
+
+ return ShadowTessellator::calculateNormal(currentVertex, nextVertex);
+}
+
+// The input z value will be converted to be non-negative inside.
+// The output must be ranged from 0 to 1.
+inline float getAlphaFromFactoredZ(float factoredZ) {
+ return 1.0 / (1 + MathUtils::max(factoredZ, 0.0f));
+}
+
+inline float getTransformedAlphaFromAlpha(float alpha) {
+ return acosf(1.0f - 2.0f * alpha);
+}
+
+// The output is ranged from 0 to M_PI.
+inline float getTransformedAlphaFromFactoredZ(float factoredZ) {
+ return getTransformedAlphaFromAlpha(getAlphaFromFactoredZ(factoredZ));
+}
+
+inline int getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2,
+ float divisor) {
+ // The formula is :
+ // extraNumber = floor(acos(dot(n1, n2)) / (M_PI / EXTRA_VERTEX_PER_PI))
+ // The value ranges for each step are:
+ // dot( ) --- [-1, 1]
+ // acos( ) --- [0, M_PI]
+ // floor(...) --- [0, EXTRA_VERTEX_PER_PI]
+ float dotProduct = vector1.dot(vector2);
+ // TODO: Use look up table for the dotProduct to extraVerticesNumber
+ // computation, if needed.
+ float angle = acosf(dotProduct);
+ return (int) floor(angle / divisor);
+}
+
+inline void checkOverflow(int used, int total, const char* bufferName) {
+ LOG_ALWAYS_FATAL_IF(used > total, "Error: %s overflow!!! used %d, total %d",
+ bufferName, used, total);
+}
+
+inline int getEdgeExtraAndUpdateSpike(Vector2* currentSpike,
+ const Vector3& secondVertex, const Vector3& centroid) {
+ Vector2 secondSpike = {secondVertex.x - centroid.x, secondVertex.y - centroid.y};
+ secondSpike.normalize();
+
+ int result = getExtraVertexNumber(secondSpike, *currentSpike, EDGE_RADIANS_DIVISOR);
+ *currentSpike = secondSpike;
+ return result;
+}
+
+// Given the caster's vertex count, compute all the buffers size depending on
+// whether or not the caster is opaque.
+inline void computeBufferSize(int* totalVertexCount, int* totalIndexCount,
+ int* totalUmbraCount, int casterVertexCount, bool isCasterOpaque) {
+ // Compute the size of the vertex buffer.
+ int outerVertexCount = casterVertexCount * 2 + MAX_EXTRA_CORNER_VERTEX_NUMBER +
+ MAX_EXTRA_EDGE_VERTEX_NUMBER;
+ int innerVertexCount = casterVertexCount + MAX_EXTRA_EDGE_VERTEX_NUMBER;
+ *totalVertexCount = outerVertexCount + innerVertexCount;
+
+ // Compute the size of the index buffer.
+ *totalIndexCount = 2 * outerVertexCount + 2;
+
+ // Compute the size of the umber buffer.
+ // For translucent object, keep track of the umbra(inner) vertex in order to draw
+ // inside. We only need to store the index information.
+ *totalUmbraCount = 0;
+ if (!isCasterOpaque) {
+ // Add the centroid if occluder is translucent.
+ *totalVertexCount++;
+ *totalIndexCount += 2 * innerVertexCount + 1;
+ *totalUmbraCount = innerVertexCount;
+ }
+}
+
+inline bool needsExtraForEdge(float firstAlpha, float secondAlpha) {
+ return abs(firstAlpha - secondAlpha) > ALPHA_THRESHOLD;
+}
+
+/**
* Calculate the shadows as a triangle strips while alpha value as the
* shadow values.
*
@@ -43,290 +166,198 @@
*
* @param shadowVertexBuffer Return an floating point array of (x, y, a)
* triangle strips mode.
+ *
+ * An simple illustration:
+ * For now let's mark the outer vertex as Pi, the inner as Vi, the centroid as C.
+ *
+ * First project the occluder to the Z=0 surface.
+ * Then we got all the inner vertices. And we compute the normal for each edge.
+ * According to the normal, we generate outer vertices. E.g: We generate P1 / P4
+ * as extra corner vertices to make the corner looks round and smoother.
+ *
+ * Due to the fact that the alpha is not linear interpolated along the inner
+ * edge, when the alpha is different, we may add extra vertices such as P2.1, P2.2,
+ * V0.1, V0.2 to avoid the visual artifacts.
+ *
+ * (P3)
+ * (P2) (P2.1) (P2.2) | ' (P4)
+ * (P1)' | | | | '
+ * ' | | | | '
+ * (P0) ------------------------------------------------(P5)
+ * | (V0) (V0.1) (V0.2) |(V1)
+ * | |
+ * | |
+ * | (C) |
+ * | |
+ * | |
+ * | |
+ * | |
+ * (V3)-----------------------------------(V2)
*/
void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
- const Vector3* vertices, int vertexCount, const Vector3& centroid3d,
+ const Vector3* casterVertices, int casterVertexCount, const Vector3& centroid3d,
float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) {
- const int rays = SHADOW_RAY_COUNT;
- // Validate the inputs.
- if (vertexCount < 3 || heightFactor <= 0 || rays <= 0
- || geomFactor <= 0) {
-#if DEBUG_SHADOW
- ALOGW("Invalid input for createAmbientShadow(), early return!");
-#endif
- return;
- }
+ shadowVertexBuffer.setMode(VertexBuffer::kIndices);
- Vector<Vector2> dir; // TODO: use C++11 unique_ptr
- dir.setCapacity(rays);
- float rayDist[rays];
- float rayHeight[rays];
- calculateRayDirections(rays, vertices, vertexCount, centroid3d, dir.editArray());
+ // In order to computer the outer vertices in one loop, we need pre-compute
+ // the normal by the vertex (n - 1) to vertex 0, and the spike and alpha value
+ // for vertex 0.
+ Vector2 previousNormal = getNormalFromVertices(casterVertices,
+ casterVertexCount - 1 , 0);
+ Vector2 currentSpike = {casterVertices[0].x - centroid3d.x,
+ casterVertices[0].y - centroid3d.y};
+ currentSpike.normalize();
+ float currentAlpha = getAlphaFromFactoredZ(casterVertices[0].z * heightFactor);
- // Calculate the length and height of the points along the edge.
- //
- // The math here is:
- // Intersect each ray (starting from the centroid) with the polygon.
- for (int i = 0; i < rays; i++) {
- int edgeIndex;
- float edgeFraction;
- float rayDistance;
- calculateIntersection(vertices, vertexCount, centroid3d, dir[i], edgeIndex,
- edgeFraction, rayDistance);
- rayDist[i] = rayDistance;
- if (edgeIndex < 0 || edgeIndex >= vertexCount) {
-#if DEBUG_SHADOW
- ALOGW("Invalid edgeIndex!");
-#endif
- edgeIndex = 0;
- }
- float h1 = vertices[edgeIndex].z;
- float h2 = vertices[((edgeIndex + 1) % vertexCount)].z;
- rayHeight[i] = h1 + edgeFraction * (h2 - h1);
- }
-
- // The output buffer length basically is roughly rays * layers, but since we
- // need triangle strips, so we need to duplicate vertices to accomplish that.
+ // Preparing all the output data.
+ int totalVertexCount, totalIndexCount, totalUmbraCount;
+ computeBufferSize(&totalVertexCount, &totalIndexCount, &totalUmbraCount,
+ casterVertexCount, isCasterOpaque);
AlphaVertex* shadowVertices =
- shadowVertexBuffer.alloc<AlphaVertex>(SHADOW_VERTEX_COUNT);
+ shadowVertexBuffer.alloc<AlphaVertex>(totalVertexCount);
+ int vertexBufferIndex = 0;
+ uint16_t* indexBuffer = shadowVertexBuffer.allocIndices<uint16_t>(totalIndexCount);
+ int indexBufferIndex = 0;
+ uint16_t umbraVertices[totalUmbraCount];
+ int umbraIndex = 0;
- // Calculate the vertex of the shadows.
- //
- // The math here is:
- // Along the edges of the polygon, for each intersection point P (generated above),
- // calculate the normal N, which should be perpendicular to the edge of the
- // polygon (represented by the neighbor intersection points) .
- // Shadow's vertices will be generated as : P + N * scale.
- const Vector2 centroid2d = {centroid3d.x, centroid3d.y};
- for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
- Vector2 normal = {1.0f, 0.0f};
- calculateNormal(rays, rayIndex, dir.array(), rayDist, normal);
+ for (int i = 0; i < casterVertexCount; i++) {
+ // Corner: first figure out the extra vertices we need for the corner.
+ const Vector3& innerVertex = casterVertices[i];
+ Vector2 currentNormal = getNormalFromVertices(casterVertices, i,
+ (i + 1) % casterVertexCount);
- // The vertex should be start from rayDist[i] then scale the
- // normalizeNormal!
- Vector2 intersection = dir[rayIndex] * rayDist[rayIndex] +
- centroid2d;
+ int extraVerticesNumber = getExtraVertexNumber(currentNormal, previousNormal,
+ CORNER_RADIANS_DIVISOR);
- // outer ring of points, expanded based upon height of each ray intersection
- float expansionDist = rayHeight[rayIndex] * heightFactor *
- geomFactor;
- AlphaVertex::set(&shadowVertices[rayIndex],
- intersection.x + normal.x * expansionDist,
- intersection.y + normal.y * expansionDist,
- 0.0f);
-
- // inner ring of points
- float opacity = 1.0 / (1 + rayHeight[rayIndex] * heightFactor);
- // NOTE: Shadow alpha values are transformed when stored in alphavertices,
- // so that they can be consumed directly by gFS_Main_ApplyVertexAlphaShadowInterp
- float transformedOpacity = acos(1.0f - 2.0f * opacity);
- AlphaVertex::set(&shadowVertices[rays + rayIndex],
- intersection.x,
- intersection.y,
- transformedOpacity);
- }
-
- if (isCasterOpaque) {
- // skip inner ring, calc bounds over filled portion of buffer
- shadowVertexBuffer.computeBounds<AlphaVertex>(2 * rays);
- shadowVertexBuffer.setMode(VertexBuffer::kOnePolyRingShadow);
- } else {
- // If caster isn't opaque, we need to to fill the umbra by storing the umbra's
- // centroid in the innermost ring of vertices.
- float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor);
- AlphaVertex centroidXYA;
- AlphaVertex::set(¢roidXYA, centroid2d.x, centroid2d.y, centroidAlpha);
- for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
- shadowVertices[2 * rays + rayIndex] = centroidXYA;
- }
- // calc bounds over entire buffer
- shadowVertexBuffer.computeBounds<AlphaVertex>();
- shadowVertexBuffer.setMode(VertexBuffer::kTwoPolyRingShadow);
- }
-
+ float expansionDist = innerVertex.z * heightFactor * geomFactor;
+ const int cornerSlicesNumber = extraVerticesNumber + 1; // Minimal as 1.
#if DEBUG_SHADOW
- for (int i = 0; i < SHADOW_VERTEX_COUNT; i++) {
- ALOGD("ambient shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x,
- shadowVertices[i].y, shadowVertices[i].alpha);
- }
+ ALOGD("cornerSlicesNumber is %d", cornerSlicesNumber);
#endif
-}
-/**
- * Generate an array of rays' direction vectors.
- * To make sure the vertices generated are clockwise, the directions are from PI
- * to -PI.
- *
- * @param rays The number of rays shooting out from the centroid.
- * @param vertices Vertices of the polygon.
- * @param vertexCount The number of vertices.
- * @param centroid3d The centroid of the polygon.
- * @param dir Return the array of ray vectors.
- */
-void AmbientShadow::calculateRayDirections(const int rays, const Vector3* vertices,
- const int vertexCount, const Vector3& centroid3d, Vector2* dir) {
- // If we don't have enough rays, then fall back to the uniform distribution.
- if (vertexCount * 2 > rays) {
- float deltaAngle = 2 * M_PI / rays;
- for (int i = 0; i < rays; i++) {
- dir[i].x = cosf(M_PI - deltaAngle * i);
- dir[i].y = sinf(M_PI - deltaAngle * i);
+ // Corner: fill the corner Vertex Buffer(VB) and Index Buffer(IB).
+ // We fill the inner vertex first, such that we can fill the index buffer
+ // inside the loop.
+ int currentInnerVertexIndex = vertexBufferIndex;
+ if (!isCasterOpaque) {
+ umbraVertices[umbraIndex++] = vertexBufferIndex;
}
- return;
- }
+ AlphaVertex::set(&shadowVertices[vertexBufferIndex++], casterVertices[i].x,
+ casterVertices[i].y,
+ getTransformedAlphaFromAlpha(currentAlpha));
- // If we have enough rays, then we assign each vertices a ray, and distribute
- // the rest uniformly.
- float rayThetas[rays];
+ const Vector3& innerStart = casterVertices[i];
- const int uniformRayCount = rays - vertexCount;
- const float deltaAngle = 2 * M_PI / uniformRayCount;
+ // outerStart is the first outer vertex for this inner vertex.
+ // outerLast is the last outer vertex for this inner vertex.
+ Vector2 outerStart = {0, 0};
+ Vector2 outerLast = {0, 0};
+ // This will create vertices from [0, cornerSlicesNumber] inclusively,
+ // which means minimally 2 vertices even without the extra ones.
+ for (int j = 0; j <= cornerSlicesNumber; j++) {
+ Vector2 averageNormal =
+ previousNormal * (cornerSlicesNumber - j) + currentNormal * j;
+ averageNormal /= cornerSlicesNumber;
+ averageNormal.normalize();
+ Vector2 outerVertex;
+ outerVertex.x = innerVertex.x + averageNormal.x * expansionDist;
+ outerVertex.y = innerVertex.y + averageNormal.y * expansionDist;
- // We have to generate all the vertices' theta anyway and we also need to
- // find the minimal, so let's precompute it first.
- // Since the incoming polygon is clockwise, we can find the dip to identify
- // the minimal theta.
- float polyThetas[vertexCount];
- int maxPolyThetaIndex = 0;
- for (int i = 0; i < vertexCount; i++) {
- polyThetas[i] = atan2(vertices[i].y - centroid3d.y,
- vertices[i].x - centroid3d.x);
- if (i > 0 && polyThetas[i] > polyThetas[i - 1]) {
- maxPolyThetaIndex = i;
- }
- }
+ indexBuffer[indexBufferIndex++] = vertexBufferIndex;
+ indexBuffer[indexBufferIndex++] = currentInnerVertexIndex;
+ AlphaVertex::set(&shadowVertices[vertexBufferIndex++], outerVertex.x,
+ outerVertex.y, OUTER_OPACITY);
- // Both poly's thetas and uniform thetas are in decrease order(clockwise)
- // from PI to -PI.
- int polyThetaIndex = maxPolyThetaIndex;
- float polyTheta = polyThetas[maxPolyThetaIndex];
- int uniformThetaIndex = 0;
- float uniformTheta = M_PI;
- for (int i = 0; i < rays; i++) {
- // Compare both thetas and pick the smaller one and move on.
- bool hasThetaCollision = abs(polyTheta - uniformTheta) < MINIMAL_DELTA_THETA;
- if (polyTheta > uniformTheta || hasThetaCollision) {
- if (hasThetaCollision) {
- // Shift the uniformTheta to middle way between current polyTheta
- // and next uniform theta. The next uniform theta can wrap around
- // to exactly PI safely here.
- // Note that neither polyTheta nor uniformTheta can be FLT_MAX
- // due to the hasThetaCollision is true.
- uniformTheta = (polyTheta + M_PI - deltaAngle * (uniformThetaIndex + 1)) / 2;
-#if DEBUG_SHADOW
- ALOGD("Shifted uniformTheta to %f", uniformTheta);
-#endif
- }
- rayThetas[i] = polyTheta;
- polyThetaIndex = (polyThetaIndex + 1) % vertexCount;
- if (polyThetaIndex != maxPolyThetaIndex) {
- polyTheta = polyThetas[polyThetaIndex];
- } else {
- // out of poly points.
- polyTheta = - FLT_MAX;
- }
- } else {
- rayThetas[i] = uniformTheta;
- uniformThetaIndex++;
- if (uniformThetaIndex < uniformRayCount) {
- uniformTheta = M_PI - deltaAngle * uniformThetaIndex;
- } else {
- // out of uniform points.
- uniformTheta = - FLT_MAX;
+ if (j == 0) {
+ outerStart = outerVertex;
+ } else if (j == cornerSlicesNumber) {
+ outerLast = outerVertex;
}
}
- }
+ previousNormal = currentNormal;
- for (int i = 0; i < rays; i++) {
+ // Edge: first figure out the extra vertices needed for the edge.
+ const Vector3& innerNext = casterVertices[(i + 1) % casterVertexCount];
+ float nextAlpha = getAlphaFromFactoredZ(innerNext.z * heightFactor);
+ if (needsExtraForEdge(currentAlpha, nextAlpha)) {
+ // TODO: See if we can / should cache this outer vertex across the loop.
+ Vector2 outerNext;
+ float expansionDist = innerNext.z * heightFactor * geomFactor;
+ outerNext.x = innerNext.x + currentNormal.x * expansionDist;
+ outerNext.y = innerNext.y + currentNormal.y * expansionDist;
+
+ // Compute the angle and see how many extra points we need.
+ int extraVerticesNumber = getEdgeExtraAndUpdateSpike(¤tSpike,
+ innerNext, centroid3d);
#if DEBUG_SHADOW
- ALOGD("No. %d : %f", i, rayThetas[i] * 180 / M_PI);
+ ALOGD("extraVerticesNumber %d for edge %d", extraVerticesNumber, i);
#endif
- // TODO: Fix the intersection precision problem and remvoe the delta added
- // here.
- dir[i].x = cosf(rayThetas[i] + MINIMAL_DELTA_THETA);
- dir[i].y = sinf(rayThetas[i] + MINIMAL_DELTA_THETA);
- }
-}
+ // Edge: fill the edge's VB and IB.
+ // This will create vertices pair from [1, extraVerticesNumber - 1].
+ // If there is no extra vertices created here, the edge will be drawn
+ // as just 2 triangles.
+ for (int k = 1; k < extraVerticesNumber; k++) {
+ int startWeight = extraVerticesNumber - k;
+ Vector2 currentOuter =
+ (outerLast * startWeight + outerNext * k) / extraVerticesNumber;
+ indexBuffer[indexBufferIndex++] = vertexBufferIndex;
+ AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentOuter.x,
+ currentOuter.y, OUTER_OPACITY);
-/**
- * Calculate the intersection of a ray hitting the polygon.
- *
- * @param vertices The shadow caster's polygon, which is represented in a
- * Vector3 array.
- * @param vertexCount The length of caster's polygon in terms of number of vertices.
- * @param start The starting point of the ray.
- * @param dir The direction vector of the ray.
- *
- * @param outEdgeIndex Return the index of the segment (or index of the starting
- * vertex) that ray intersect with.
- * @param outEdgeFraction Return the fraction offset from the segment starting
- * index.
- * @param outRayDist Return the ray distance from centroid to the intersection.
- */
-void AmbientShadow::calculateIntersection(const Vector3* vertices, int vertexCount,
- const Vector3& start, const Vector2& dir, int& outEdgeIndex,
- float& outEdgeFraction, float& outRayDist) {
- float startX = start.x;
- float startY = start.y;
- float dirX = dir.x;
- float dirY = dir.y;
- // Start the search from the last edge from poly[len-1] to poly[0].
- int p1 = vertexCount - 1;
-
- for (int p2 = 0; p2 < vertexCount; p2++) {
- float p1x = vertices[p1].x;
- float p1y = vertices[p1].y;
- float p2x = vertices[p2].x;
- float p2y = vertices[p2].y;
-
- // The math here is derived from:
- // f(t, v) = p1x * (1 - t) + p2x * t - (startX + dirX * v) = 0;
- // g(t, v) = p1y * (1 - t) + p2y * t - (startY + dirY * v) = 0;
- float div = (dirX * (p1y - p2y) + dirY * p2x - dirY * p1x);
- if (div != 0) {
- float t = (dirX * (p1y - startY) + dirY * startX - dirY * p1x) / (div);
- if (t > 0 && t <= 1) {
- float t2 = (p1x * (startY - p2y)
- + p2x * (p1y - startY)
- + startX * (p2y - p1y)) / div;
- if (t2 > 0) {
- outEdgeIndex = p1;
- outRayDist = t2;
- outEdgeFraction = t;
- return;
+ if (!isCasterOpaque) {
+ umbraVertices[umbraIndex++] = vertexBufferIndex;
}
+ Vector3 currentInner =
+ (innerStart * startWeight + innerNext * k) / extraVerticesNumber;
+ indexBuffer[indexBufferIndex++] = vertexBufferIndex;
+ AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentInner.x,
+ currentInner.y,
+ getTransformedAlphaFromFactoredZ(currentInner.z * heightFactor));
}
}
- p1 = p2;
+ currentAlpha = nextAlpha;
}
- return;
-};
-/**
- * Calculate the normal at the intersection point between a ray and the polygon.
- *
- * @param rays The total number of rays.
- * @param currentRayIndex The index of the ray which the normal is based on.
- * @param dir The array of the all the rays directions.
- * @param rayDist The pre-computed ray distances array.
- *
- * @param normal Return the normal.
- */
-void AmbientShadow::calculateNormal(int rays, int currentRayIndex,
- const Vector2* dir, const float* rayDist, Vector2& normal) {
- int preIndex = (currentRayIndex - 1 + rays) % rays;
- int postIndex = (currentRayIndex + 1) % rays;
- Vector2 p1 = dir[preIndex] * rayDist[preIndex];
- Vector2 p2 = dir[postIndex] * rayDist[postIndex];
+ indexBuffer[indexBufferIndex++] = 1;
+ indexBuffer[indexBufferIndex++] = 0;
- // Now the rays are going CW around the poly.
- Vector2 delta = p2 - p1;
- if (delta.length() != 0) {
- delta.normalize();
- // Calculate the normal , which is CCW 90 rotate to the delta.
- normal.x = - delta.y;
- normal.y = delta.x;
+ if (!isCasterOpaque) {
+ // Add the centroid as the last one in the vertex buffer.
+ float centroidOpacity =
+ getTransformedAlphaFromFactoredZ(centroid3d.z * heightFactor);
+ int centroidIndex = vertexBufferIndex;
+ AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid3d.x,
+ centroid3d.y, centroidOpacity);
+
+ for (int i = 0; i < umbraIndex; i++) {
+ // Note that umbraVertices[0] is always 0.
+ // So the start and the end of the umbra are using the "0".
+ // And penumbra ended with 0, so a degenerated triangle is formed b/t
+ // the umbra and penumbra.
+ indexBuffer[indexBufferIndex++] = umbraVertices[i];
+ indexBuffer[indexBufferIndex++] = centroidIndex;
+ }
+ indexBuffer[indexBufferIndex++] = 0;
}
+
+ // At the end, update the real index and vertex buffer size.
+ shadowVertexBuffer.updateVertexCount(vertexBufferIndex);
+ shadowVertexBuffer.updateIndexCount(indexBufferIndex);
+
+ checkOverflow(vertexBufferIndex, totalVertexCount, "Vertex Buffer");
+ checkOverflow(indexBufferIndex, totalIndexCount, "Index Buffer");
+ checkOverflow(umbraIndex, totalUmbraCount, "Umbra Buffer");
+
+#if DEBUG_SHADOW
+ for (int i = 0; i < vertexBufferIndex; i++) {
+ ALOGD("vertexBuffer i %d, (%f, %f %f)", i, shadowVertices[i].x, shadowVertices[i].y,
+ shadowVertices[i].alpha);
+ }
+ for (int i = 0; i < indexBufferIndex; i++) {
+ ALOGD("indexBuffer i %d, indexBuffer[i] %d", i, indexBuffer[i]);
+ }
+#endif
}
}; // namespace uirenderer
diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h
index 68df246..9660dc0 100644
--- a/libs/hwui/AmbientShadow.h
+++ b/libs/hwui/AmbientShadow.h
@@ -28,27 +28,12 @@
/**
* AmbientShadow is used to calculate the ambient shadow value around a polygon.
- *
- * TODO: calculateIntersection() now is O(N*M), where N is the number of
- * polygon's vertics and M is the number of rays. In fact, by staring tracing
- * the vertex from the previous intersection, the algorithm can be O(N + M);
*/
class AmbientShadow {
public:
static void createAmbientShadow(bool isCasterOpaque, const Vector3* poly,
int polyLength, const Vector3& centroid3d, float heightFactor,
float geomFactor, VertexBuffer& shadowVertexBuffer);
-
-private:
- static void calculateRayDirections(const int rays, const Vector3* vertices,
- const int vertexCount, const Vector3& centroid3d, Vector2* dir);
-
- static void calculateIntersection(const Vector3* poly, int nbVertices,
- const Vector3& start, const Vector2& dir, int& outEdgeIndex,
- float& outEdgeFraction, float& outRayDist);
-
- static void calculateNormal(int rays, int currentRayIndex, const Vector2* dir,
- const float* rayDist, Vector2& normal);
}; // AmbientShadow
}; // namespace uirenderer
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 1c697d5..da65f38 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -95,6 +95,8 @@
// Oh boy, we're starting! Man the battle stations!
if (mPlayState == RUNNING) {
transitionToRunning(context);
+ } else if (mPlayState == FINISHED) {
+ callOnFinishedListener(context);
}
}
}
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index bbf0551..0f36c06 100755
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2417,6 +2417,10 @@
} else if (mode == VertexBuffer::kTwoPolyRingShadow) {
mCaches.bindShadowIndicesBuffer();
glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
+ } else if (mode == VertexBuffer::kIndices) {
+ mCaches.unbindIndicesBuffer();
+ glDrawElements(GL_TRIANGLE_STRIP, vertexBuffer.getIndexCount(), GL_UNSIGNED_SHORT,
+ vertexBuffer.getIndices());
}
if (isAA) {
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index 2a9f01c..d033ed9 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -111,6 +111,23 @@
float y;
float z;
+ Vector3 operator+(const Vector3& v) const {
+ return (Vector3){x + v.x, y + v.y, z + v.z};
+ }
+
+ Vector3 operator-(const Vector3& v) const {
+ return (Vector3){x - v.x, y - v.y, z - v.z};
+ }
+
+ Vector3 operator/(float s) const {
+ return (Vector3){x / s, y / s, z / s};
+ }
+
+ Vector3 operator*(float s) const {
+ return (Vector3){x * s, y * s, z * s};
+ }
+
+
void dump() {
ALOGD("Vector3[%.2f, %.2f, %.2f]", x, y, z);
}
diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h
index 3837f88..966fa4e 100644
--- a/libs/hwui/VertexBuffer.h
+++ b/libs/hwui/VertexBuffer.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_HWUI_VERTEX_BUFFER_H
#define ANDROID_HWUI_VERTEX_BUFFER_H
+#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
@@ -26,19 +27,27 @@
enum Mode {
kStandard = 0,
kOnePolyRingShadow = 1,
- kTwoPolyRingShadow = 2
+ kTwoPolyRingShadow = 2,
+ kIndices = 3
};
VertexBuffer()
: mBuffer(0)
+ , mIndices(0)
, mVertexCount(0)
+ , mIndexCount(0)
+ , mAllocatedVertexCount(0)
+ , mAllocatedIndexCount(0)
, mByteCount(0)
, mMode(kStandard)
+ , mReallocBuffer(0)
, mCleanupMethod(NULL)
+ , mCleanupIndexMethod(NULL)
{}
~VertexBuffer() {
if (mCleanupMethod) mCleanupMethod(mBuffer);
+ if (mCleanupIndexMethod) mCleanupIndexMethod(mIndices);
}
/**
@@ -59,6 +68,7 @@
mReallocBuffer = reallocBuffer + vertexCount;
return reallocBuffer;
}
+ mAllocatedVertexCount = vertexCount;
mVertexCount = vertexCount;
mByteCount = mVertexCount * sizeof(TYPE);
mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount];
@@ -69,6 +79,17 @@
}
template <class TYPE>
+ TYPE* allocIndices(int indexCount) {
+ mAllocatedIndexCount = indexCount;
+ mIndexCount = indexCount;
+ mIndices = (void*)new TYPE[indexCount];
+
+ mCleanupIndexMethod = &(cleanup<TYPE>);
+
+ return (TYPE*)mIndices;
+ }
+
+ template <class TYPE>
void copyInto(const VertexBuffer& srcBuffer, float xOffset, float yOffset) {
int verticesToCopy = srcBuffer.getVertexCount();
@@ -103,9 +124,17 @@
}
const void* getBuffer() const { return mBuffer; }
+ const void* getIndices() const { return mIndices; }
const Rect& getBounds() const { return mBounds; }
unsigned int getVertexCount() const { return mVertexCount; }
unsigned int getSize() const { return mByteCount; }
+ unsigned int getIndexCount() const { return mIndexCount; }
+ void updateIndexCount(unsigned int newCount) {
+ mIndexCount = MathUtils::min(newCount, mAllocatedIndexCount);
+ }
+ void updateVertexCount(unsigned int newCount) {
+ newCount = MathUtils::min(newCount, mAllocatedVertexCount);
+ }
Mode getMode() const { return mMode; }
void setBounds(Rect bounds) { mBounds = bounds; }
@@ -127,14 +156,22 @@
}
Rect mBounds;
+
void* mBuffer;
+ void* mIndices;
+
unsigned int mVertexCount;
+ unsigned int mIndexCount;
+ unsigned int mAllocatedVertexCount;
+ unsigned int mAllocatedIndexCount;
unsigned int mByteCount;
+
Mode mMode;
void* mReallocBuffer; // used for multi-allocation
void (*mCleanupMethod)(void*);
+ void (*mCleanupIndexMethod)(void*);
};
}; // namespace uirenderer
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index ecfedf6..491a295 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -314,6 +314,7 @@
stopDrawing();
if (mEglManager.hasEglContext()) {
requireGlContext();
+ freePrefetechedLayers();
mRootRenderNode->destroyHardwareResources();
Caches::getInstance().flush(Caches::kFlushMode_Layers);
}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 711831b..ae8ce4b 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -683,6 +683,7 @@
*
* @param mediaButtonIntent an intent containing the KeyEvent as an
* extra
+ * @return True if the event was handled, false otherwise.
*/
public boolean onMediaButtonEvent(@NonNull Intent mediaButtonIntent) {
if (mSession != null
@@ -695,36 +696,43 @@
case KeyEvent.KEYCODE_MEDIA_PLAY:
if ((validActions & PlaybackState.ACTION_PLAY) != 0) {
onPlay();
+ return true;
}
break;
case KeyEvent.KEYCODE_MEDIA_PAUSE:
if ((validActions & PlaybackState.ACTION_PAUSE) != 0) {
onPause();
+ return true;
}
break;
case KeyEvent.KEYCODE_MEDIA_NEXT:
if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) {
onSkipToNext();
+ return true;
}
break;
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
if ((validActions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0) {
onSkipToPrevious();
+ return true;
}
break;
case KeyEvent.KEYCODE_MEDIA_STOP:
if ((validActions & PlaybackState.ACTION_STOP) != 0) {
onStop();
+ return true;
}
break;
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
if ((validActions & PlaybackState.ACTION_FAST_FORWARD) != 0) {
onFastForward();
+ return true;
}
break;
case KeyEvent.KEYCODE_MEDIA_REWIND:
if ((validActions & PlaybackState.ACTION_REWIND) != 0) {
onRewind();
+ return true;
}
break;
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@@ -737,8 +745,10 @@
| PlaybackState.ACTION_PAUSE)) != 0;
if (isPlaying && canPause) {
onPause();
+ return true;
} else if (!isPlaying && canPlay) {
onPlay();
+ return true;
}
break;
}
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index 0bda2b3..ccc6ad8 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -136,7 +136,7 @@
* <table>
* <tr>
* <th>Constant Value</th>
- * <th>Comment</th>
+ * <th>Description</th>
* </tr>
* <tr>
* <td>com.android.tv</td>
@@ -148,7 +148,7 @@
* <table>
* <tr>
* <th>Constant Value</th>
- * <th>Comment</th>
+ * <th>Description</th>
* </tr>
* <tr>
* <td>AM_TV_RS</td>
@@ -346,7 +346,7 @@
* <tr>
* <th>Rating System</th>
* <th>Constant Value</th>
- * <th>Comment</th>
+ * <th>Description</th>
* </tr>
* <tr>
* <td valign="top" rowspan="6">AM_TV_RS</td>
@@ -1419,7 +1419,7 @@
* <tr>
* <th>Rating System</th>
* <th>Constant Value</th>
- * <th>Comment</th>
+ * <th>Description</th>
* </tr>
* <tr>
* <td valign="top" rowspan="6">NL_TV</td>
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 00c7ad4..7f1c304 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -680,7 +680,7 @@
* <p>
* A value of 1 indicates the channel is included in the channel list that applications use
* to browse channels, a value of 0 indicates the channel is not included in the list. If
- * not specified, this value is set to 1 (browsable) by default.
+ * not specified, this value is set to 0 (not browsable) by default.
* </p><p>
* Type: INTEGER (boolean)
* </p>
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index c678ac7..0fed27e 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -263,7 +263,7 @@
String8 vendorMessage;
if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
- vendorMessage.format("DRM vendor-defined error: %d", err);
+ vendorMessage = String8::format("DRM vendor-defined error: %d", err);
drmMessage = vendorMessage.string();
}
@@ -285,7 +285,7 @@
if (msg == NULL) {
msg = drmMessage;
} else {
- errbuf.format("%s: %s", msg, drmMessage);
+ errbuf = String8::format("%s: %s", msg, drmMessage);
msg = errbuf.string();
}
}
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index ee5d42a..761d58a 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -27,6 +27,7 @@
android:id="@+id/static_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
+ android:paddingStart="8dip"
android:elevation="@dimen/preview_controls_elevation"
android:background="?android:attr/colorPrimary">
diff --git a/packages/PrintSpooler/res/layout/print_activity_controls.xml b/packages/PrintSpooler/res/layout/print_activity_controls.xml
index 0629481..c2a0da9 100644
--- a/packages/PrintSpooler/res/layout/print_activity_controls.xml
+++ b/packages/PrintSpooler/res/layout/print_activity_controls.xml
@@ -55,8 +55,7 @@
android:text="@string/label_copies">
</TextView>
- <view
- class="com.android.printspooler.widget.FirstFocusableEditText"
+ <EditText
android:id="@+id/copies_edittext"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
@@ -64,7 +63,7 @@
android:singleLine="true"
android:ellipsize="end"
android:inputType="numberDecimal">
- </view>
+ </EditText>
</LinearLayout>
@@ -198,8 +197,7 @@
android:visibility="visible">
</TextView>
- <view
- class="com.android.printspooler.widget.FirstFocusableEditText"
+ <EditText
android:id="@+id/page_range_edittext"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
@@ -208,7 +206,7 @@
android:ellipsize="end"
android:visibility="visible"
android:inputType="textNoSuggestions">
- </view>
+ </EditText>
</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
index 43d8aaf..4381a7a 100644
--- a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
+++ b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
@@ -17,8 +17,8 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:paddingStart="16dip"
- android:paddingEnd="16dip"
+ android:paddingStart="8dip"
+ android:paddingEnd="8dip"
android:minHeight="56dip"
android:orientation="horizontal"
android:gravity="start|center_vertical">
diff --git a/packages/PrintSpooler/res/layout/select_printer_activity.xml b/packages/PrintSpooler/res/layout/select_printer_activity.xml
index 173057b..77c500a 100644
--- a/packages/PrintSpooler/res/layout/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/layout/select_printer_activity.xml
@@ -23,8 +23,6 @@
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:paddingStart="@dimen/printer_list_view_padding_start"
- android:paddingEnd="@dimen/printer_list_view_padding_end"
android:scrollbarStyle="outsideOverlay"
android:cacheColorHint="@android:color/transparent"
android:scrollbarAlwaysDrawVerticalTrack="true" >
diff --git a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
deleted file mode 100644
index 14403a1..0000000
--- a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:orientation="vertical"
- android:gravity="start|center_vertical">
-
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="?android:attr/spinnerDropDownItemStyle"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?android:attr/textColorPrimary"
- android:singleLine="true"
- android:ellipsize="end"
- android:textIsSelectable="false"
- android:gravity="top|left"
- android:duplicateParentState="true">
- </TextView>
-
- <TextView
- android:id="@+id/subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="?android:attr/spinnerDropDownItemStyle"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorPrimary"
- android:singleLine="true"
- android:ellipsize="end"
- android:textIsSelectable="false"
- android:visibility="gone"
- android:duplicateParentState="true">
- </TextView>
-
-</LinearLayout>
diff --git a/packages/PrintSpooler/res/values-land/constants.xml b/packages/PrintSpooler/res/values-land/constants.xml
index a4666a5..6cf9754b5 100644
--- a/packages/PrintSpooler/res/values-land/constants.xml
+++ b/packages/PrintSpooler/res/values-land/constants.xml
@@ -16,10 +16,6 @@
<resources>
- <dimen name="printer_list_view_padding_start">48dip</dimen>
-
- <dimen name="printer_list_view_padding_end">48dip</dimen>
-
<integer name="preview_page_per_row_count">2</integer>
<integer name="print_option_column_count">3</integer>
diff --git a/packages/PrintSpooler/res/values/constants.xml b/packages/PrintSpooler/res/values/constants.xml
index b95703b..b4e4777 100644
--- a/packages/PrintSpooler/res/values/constants.xml
+++ b/packages/PrintSpooler/res/values/constants.xml
@@ -28,9 +28,6 @@
<dimen name="print_dialog_frame_max_width_dip">400dip</dimen>
- <dimen name="printer_list_view_padding_start">16dip</dimen>
- <dimen name="printer_list_view_padding_end">16dip</dimen>
-
<dimen name="selected_page_elevation">6dip</dimen>
<dimen name="unselected_page_elevation">2dip</dimen>
@@ -46,6 +43,6 @@
<fraction name="page_unselected_alpha">50%</fraction>
<dimen name="preview_page_footer_height">32dip</dimen>
- <dimen name="preview_page_min_width">130dip</dimen>
+ <dimen name="preview_page_min_width">128dip</dimen>
</resources>
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index db319e9..532b01f 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -16,7 +16,10 @@
<resources>
- <style name="PrintActivity" parent="@android:style/Theme.Material.Settings">
+ <style name="PrintActivity" parent="@android:style/Theme.Material">
+ <item name="android:colorPrimary">@*android:color/material_blue_grey_900</item>
+ <item name="android:colorPrimaryDark">@*android:color/material_blue_grey_950</item>
+ <item name="android:colorAccent">@*android:color/material_deep_teal_500</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index 5bcdb9f..d949673 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -289,15 +289,11 @@
+ " for position: " + position);
}
- final int pageCount = getItemCount();
MyViewHolder myHolder = (MyViewHolder) holder;
View page = holder.itemView;
- if (pageCount > 1) {
- page.setOnClickListener(mPageClickListener);
- } else {
- page.setOnClickListener(null);
- }
+ page.setOnClickListener(mPageClickListener);
+
page.setTag(holder);
myHolder.mPageInAdapter = position;
@@ -339,16 +335,9 @@
}
content.init(provider, mMediaSize, mMinMargins);
-
View pageSelector = page.findViewById(R.id.page_selector);
pageSelector.setTag(myHolder);
- if (pageCount > 1) {
- pageSelector.setOnClickListener(mPageClickListener);
- pageSelector.setVisibility(View.VISIBLE);
- } else {
- pageSelector.setOnClickListener(null);
- pageSelector.setVisibility(View.GONE);
- }
+ pageSelector.setOnClickListener(mPageClickListener);
if (mConfirmedPagesInDocument.indexOfKey(pageInDocument) >= 0) {
pageSelector.setSelected(true);
@@ -449,8 +438,9 @@
final int verticalPadding;
if (mPageContentHeight + mFooterHeight + mPreviewListPadding > availableHeight) {
- verticalPadding = Math.max(mPreviewPageMargin,
- (availableHeight - totalContentHeight) / 2);
+ verticalPadding = Math.max(0,
+ (availableHeight - mPageContentHeight - mFooterHeight) / 2
+ - mPreviewPageMargin);
} else {
verticalPadding = Math.max(mPreviewListPadding,
(availableHeight - totalContentHeight) / 2);
@@ -791,6 +781,9 @@
page.animate().translationZ(mSelectedPageElevation)
.alpha(mSelectedPageAlpha);
} else {
+ if (mConfirmedPagesInDocument.size() <= 1) {
+ return;
+ }
mConfirmedPagesInDocument.remove(pageInDocument);
pageSelector.setSelected(false);
page.animate().translationZ(mUnselectedPageElevation)
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index fe17516..01c9746 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -982,21 +982,21 @@
// Media size.
mMediaSizeSpinnerAdapter = new ArrayAdapter<>(
- this, R.layout.spinner_dropdown_item, R.id.title);
+ this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
mMediaSizeSpinner = (Spinner) findViewById(R.id.paper_size_spinner);
mMediaSizeSpinner.setAdapter(mMediaSizeSpinnerAdapter);
mMediaSizeSpinner.setOnItemSelectedListener(itemSelectedListener);
// Color mode.
mColorModeSpinnerAdapter = new ArrayAdapter<>(
- this, R.layout.spinner_dropdown_item, R.id.title);
+ this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
mColorModeSpinner = (Spinner) findViewById(R.id.color_spinner);
mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter);
mColorModeSpinner.setOnItemSelectedListener(itemSelectedListener);
// Orientation
mOrientationSpinnerAdapter = new ArrayAdapter<>(
- this, R.layout.spinner_dropdown_item, R.id.title);
+ this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
String[] orientationLabels = getResources().getStringArray(
R.array.orientation_labels);
mOrientationSpinnerAdapter.add(new SpinnerItem<>(
@@ -1008,8 +1008,8 @@
mOrientationSpinner.setOnItemSelectedListener(itemSelectedListener);
// Range options
- ArrayAdapter<SpinnerItem<Integer>> rangeOptionsSpinnerAdapter =
- new ArrayAdapter<>(this, R.layout.spinner_dropdown_item, R.id.title);
+ ArrayAdapter<SpinnerItem<Integer>> rangeOptionsSpinnerAdapter = new ArrayAdapter<>(
+ this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
mRangeOptionsSpinner = (Spinner) findViewById(R.id.range_options_spinner);
mRangeOptionsSpinner.setAdapter(rangeOptionsSpinnerAdapter);
mRangeOptionsSpinner.setOnItemSelectedListener(itemSelectedListener);
@@ -1075,6 +1075,7 @@
mDestinationSpinner.setEnabled(false);
}
mCopiesEditText.setEnabled(false);
+ mCopiesEditText.setFocusable(false);
mMediaSizeSpinner.setEnabled(false);
mColorModeSpinner.setEnabled(false);
mOrientationSpinner.setEnabled(false);
@@ -1089,6 +1090,7 @@
// available, we disable all print options except the destination.
if (mCurrentPrinter == null || !canPrint(mCurrentPrinter)) {
mCopiesEditText.setEnabled(false);
+ mCopiesEditText.setFocusable(false);
mMediaSizeSpinner.setEnabled(false);
mColorModeSpinner.setEnabled(false);
mOrientationSpinner.setEnabled(false);
@@ -1316,8 +1318,10 @@
// Copies
if (mDestinationSpinnerAdapter.getPdfPrinter() != mCurrentPrinter) {
mCopiesEditText.setEnabled(true);
+ mCopiesEditText.setFocusableInTouchMode(true);
} else {
mCopiesEditText.setEnabled(false);
+ mCopiesEditText.setFocusable(false);
}
if (mCopiesEditText.getError() == null
&& TextUtils.isEmpty(mCopiesEditText.getText())) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/FirstFocusableEditText.java b/packages/PrintSpooler/src/com/android/printspooler/widget/FirstFocusableEditText.java
deleted file mode 100644
index d6bb7c8..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/FirstFocusableEditText.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.printspooler.widget;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.widget.EditText;
-
-/**
- * An instance of this class class is intended to be the first focusable
- * in a layout to which the system automatically gives focus. It performs
- * some voodoo to avoid the first tap on it to start an edit mode, rather
- * to bring up the IME, i.e. to get the behavior as if the view was not
- * focused.
- */
-public final class FirstFocusableEditText extends EditText {
- private boolean mClickedBeforeFocus;
- private CharSequence mError;
-
- public FirstFocusableEditText(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public boolean performClick() {
- super.performClick();
- if (isFocused() && !mClickedBeforeFocus) {
- clearFocus();
- requestFocus();
- }
- mClickedBeforeFocus = true;
- return true;
- }
-
- @Override
- public CharSequence getError() {
- return mError;
- }
-
- @Override
- public void setError(CharSequence error, Drawable icon) {
- setCompoundDrawables(null, null, icon, null);
- mError = error;
- }
-
- protected void onFocusChanged(boolean gainFocus, int direction,
- Rect previouslyFocusedRect) {
- if (!gainFocus) {
- mClickedBeforeFocus = false;
- }
- super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
- }
-}
\ No newline at end of file
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
index e428948..c84b06a 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
@@ -152,6 +152,17 @@
// Make sure we start in a closed options state.
onDragProgress(1.0f);
+
+ // The framework gives focus to the frist focusable and we
+ // do not want that, hence we will take focus instead.
+ setFocusableInTouchMode(true);
+ }
+
+ @Override
+ public void focusableViewAvailable(View v) {
+ // The framework gives focus to the frist focusable and we
+ // do not want that, hence do not announce new focusables.
+ return;
}
@Override
@@ -309,6 +320,7 @@
mSummaryContent.setLayerType(View.LAYER_TYPE_NONE, null);
mDraggableContent.setLayerType(View.LAYER_TYPE_NONE, null);
mMoreOptionsButton.setLayerType(View.LAYER_TYPE_NONE, null);
+ mMoreOptionsButton.setLayerType(View.LAYER_TYPE_NONE, null);
}
mDragProgress = progress;
@@ -320,7 +332,6 @@
mMoreOptionsButton.setAlpha(inverseAlpha);
mEmbeddedContentScrim.setBackgroundColor(computeScrimColor());
-
if (progress == 0) {
if (mOptionsStateChangeListener != null) {
mOptionsStateChangeListener.onOptionsOpened();
@@ -354,14 +365,15 @@
}
private void ensureImeClosedAndInputFocusCleared() {
- View focus = findFocus();
- if (focus != null) {
+ View focused = findFocus();
+
+ if (focused != null && focused.isFocused()) {
InputMethodManager imm = (InputMethodManager) mContext.getSystemService(
Context.INPUT_METHOD_SERVICE);
- if (imm.isActive(focus)) {
+ if (imm.isActive(focused)) {
imm.hideSoftInputFromWindow(getWindowToken(), 0);
}
- focus.clearFocus();
+ focused.clearFocus();
}
}
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 299e50c..a3bed4f 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -187,6 +187,9 @@
<!-- Default for Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1==on -->
<integer name="def_lock_screen_show_notifications">1</integer>
+ <!-- Default for Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS -->
+ <bool name="def_lock_screen_allow_private_notifications">true</bool>
+
<!-- Default for Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, 1==on -->
<integer name="def_heads_up_enabled">1</integer>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index edefb13..fd5e6fe 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -70,7 +70,7 @@
// database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
// is properly propagated through your change. Not doing so will result in a loss of user
// settings.
- private static final int DATABASE_VERSION = 108;
+ private static final int DATABASE_VERSION = 109;
private Context mContext;
private int mUserHandle;
@@ -1673,8 +1673,8 @@
try {
stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
+ " VALUES(?,?);");
- loadBooleanSetting(stmt, Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
- R.bool.def_guest_user_enabled);
+ loadIntegerSetting(stmt, Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ R.integer.def_lock_screen_show_notifications);
if (mUserHandle == UserHandle.USER_OWNER) {
final int oldShow = getIntValueFromTable(db,
TABLE_GLOBAL, Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, -1);
@@ -1733,6 +1733,22 @@
upgradeVersion = 108;
}
+ if (upgradeVersion < 109) {
+ db.beginTransaction();
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
+ + " VALUES(?,?);");
+ loadBooleanSetting(stmt, Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ R.bool.def_lock_screen_allow_private_notifications);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ if (stmt != null) stmt.close();
+ }
+ upgradeVersion = 109;
+ }
+
// *** Remember to update DATABASE_VERSION above!
if (upgradeVersion != currentVersion) {
@@ -2301,6 +2317,9 @@
loadIntegerSetting(stmt, Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
R.integer.def_lock_screen_show_notifications);
+ loadBooleanSetting(stmt, Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ R.bool.def_lock_screen_allow_private_notifications);
+
} finally {
if (stmt != null) stmt.close();
}
diff --git a/packages/SystemUI/res/drawable/ic_android.xml b/packages/SystemUI/res/drawable/ic_android.xml
new file mode 100644
index 0000000..19ee9a7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_android.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M6.000000,18.000000c0.000000,0.600000 0.400000,1.000000 1.000000,1.000000l1.000000,0.000000l0.000000,3.500000C8.000000,23.299999 8.700000,24.000000 9.500000,24.000000c0.800000,0.000000 1.500000,-0.700000 1.500000,-1.500000L11.000000,19.000000l2.000000,0.000000l0.000000,3.500000c0.000000,0.800000 0.700000,1.500000 1.500000,1.500000c0.800000,0.000000 1.500000,-0.700000 1.500000,-1.500000L16.000000,19.000000l1.000000,0.000000c0.600000,0.000000 1.000000,-0.400000 1.000000,-1.000000L18.000000,8.000000L6.000000,8.000000L6.000000,18.000000zM3.500000,8.000000C2.700000,8.000000 2.000000,8.700000 2.000000,9.500000l0.000000,7.000000C2.000000,17.299999 2.700000,18.000000 3.500000,18.000000C4.300000,18.000000 5.000000,17.299999 5.000000,16.500000l0.000000,-7.000000C5.000000,8.700000 4.300000,8.000000 3.500000,8.000000zM20.500000,8.000000C19.700001,8.000000 19.000000,8.700000 19.000000,9.500000l0.000000,7.000000c0.000000,0.800000 0.700000,1.500000 1.500000,1.500000c0.800000,0.000000 1.500000,-0.700000 1.500000,-1.500000l0.000000,-7.000000C22.000000,8.700000 21.299999,8.000000 20.500000,8.000000zM15.500000,2.200000l1.300000,-1.300000c0.200000,-0.200000 0.200000,-0.500000 0.000000,-0.700000c-0.200000,-0.200000 -0.500000,-0.200000 -0.700000,0.000000l-1.500000,1.500000C13.900000,1.200000 13.000000,1.000000 12.000000,1.000000c-1.000000,0.000000 -1.900000,0.200000 -2.700000,0.600000L7.900000,0.100000C7.700000,0.000000 7.300000,0.000000 7.100000,0.100000C7.000000,0.300000 7.000000,0.700000 7.100000,0.900000l1.300000,1.300000C7.000000,3.300000 6.000000,5.000000 6.000000,7.000000l12.000000,0.000000C18.000000,5.000000 17.000000,3.200000 15.500000,2.200000zM10.000000,5.000000L9.000000,5.000000L9.000000,4.000000l1.000000,0.000000L10.000000,5.000000zM15.000000,5.000000l-1.000000,0.000000L14.000000,4.000000l1.000000,0.000000L15.000000,5.000000z"
+ android:fillColor="#ffffffff"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_close.xml b/packages/SystemUI/res/drawable/ic_close.xml
new file mode 100644
index 0000000..7d93d45
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_close.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
+ android:fillColor="#FF000000"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index ef0c9bb..ca07c87 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -67,6 +67,6 @@
android:src="@drawable/ic_lock_24dp"
android:scaleType="center"
android:tint="#ffffffff"
- android:contentDescription="@string/accessibility_unlock_button_not_secured" />
+ android:contentDescription="@string/accessibility_unlock_button" />
</com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
index eff3758..351177b 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -38,8 +38,8 @@
android:id="@+id/more_text"
android:layout_width="32dp"
android:layout_height="32dp"
- android:layout_marginStart="20dp"
- android:layout_marginEnd="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="12dp"
android:layout_gravity="center_vertical"
android:background="@drawable/keyguard_overflow_number_background"
android:gravity="center"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b000a48..b488c56 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -213,6 +213,8 @@
<string name="accessibility_camera_button">Camera</string>
<!-- Content description of the phone button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_phone_button">Phone</string>
+ <!-- Content description of the unlock button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_unlock_button">Unlock</string>
<!-- Click action label for accessibility for the unlock button. [CHAR LIMIT=NONE] -->
<string name="unlock_label">unlock</string>
<!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
@@ -220,17 +222,6 @@
<!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
<string name="camera_label">open camera</string>
- <!-- Content description of the lock icon when device is secured (lock closed) and trust not managed (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_unlock_button_secured">Device secured.</string>
- <!-- Content description of the lock icon when device is not secured (lock open) and trust not managed (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_unlock_button_not_secured">Device not secured.</string>
- <!-- Content description of the lock icon when device is secured (lock closed) and trust managed (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_unlock_button_secured_trust_managed">Device secured, trust agent active.</string>
- <!-- Content description of the lock icon when device is not secured (lock open) and trust managed (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_unlock_button_not_secured_trust_managed">Device not secured, trust agent active.</string>
- <!-- Content description of the lock icon when face unlock is running (face icon) and trust managed (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_unlock_button_face_unlock_running">Face detection running, trust agent active.</string>
-
<!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_ime_switch_button">Switch input method button.</string>
<!-- Content description of the compatibility zoom button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -323,6 +314,15 @@
<!-- Content description of an item with full signal for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_signal_full">Signal full.</string>
+ <!-- Content description of an item that is turned on for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_desc_on">On.</string>
+ <!-- Content description of an item that is turned off for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_desc_off">Off.</string>
+ <!-- Content description of an item that is connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_desc_connected">Connected.</string>
+ <!-- Content description of an item that is connecting for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_desc_connecting">Connecting.</string>
+
<!-- Content description of the data connection type GPRS for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_data_connection_gprs">GPRS</string>
@@ -894,6 +894,18 @@
<!-- Indication on the keyguard that appears when the user disables trust agents until the next time they unlock manually. [CHAR LIMIT=NONE] -->
<string name="keyguard_indication_trust_disabled">Device will stay locked until you manually unlock</string>
+ <!-- Title of notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=40] -->
+ <string name="hidden_notifications_title">Get notifications faster</string>
+
+ <!-- Body of notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=60] -->
+ <string name="hidden_notifications_text">See them before you unlock</string>
+
+ <!-- Cancel action for notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=10] -->
+ <string name="hidden_notifications_cancel">No thanks</string>
+
+ <!-- continue action for notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=10] -->
+ <string name="hidden_notifications_setup">Set up</string>
+
<!-- Indication that the current volume and other effects (vibration) are being suppressed by a third party, such as a notification listener. [CHAR LIMIT=30] -->
<string name="muted_by">Muted by <xliff:g id="third_party">%1$s</xliff:g></string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 1ac3bc3..0c6e7b6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -67,6 +67,7 @@
ArrayList<TaskStack> mStacks;
View mSearchBar;
RecentsViewCallbacks mCb;
+ boolean mAlreadyLaunchingTask;
public RecentsView(Context context) {
super(context);
@@ -120,6 +121,9 @@
}
addView(stackView);
}
+
+ // Reset the launched state
+ mAlreadyLaunchingTask = false;
}
/** Removes all the task stack views from this recents view. */
@@ -381,6 +385,11 @@
if (mCb != null) {
mCb.onTaskViewClicked();
}
+ // Skip if we are already launching tasks
+ if (mAlreadyLaunchingTask) {
+ return;
+ }
+ mAlreadyLaunchingTask = true;
// Upfront the processing of the thumbnail
TaskViewTransform transform = new TaskViewTransform();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index da99118..f04c8c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -22,6 +22,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.app.admin.DevicePolicyManager;
@@ -35,7 +36,11 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.database.ContentObserver;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
@@ -78,6 +83,7 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
import com.android.internal.util.NotificationColorUtil;
+import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SearchPanelView;
@@ -126,6 +132,12 @@
public static final int EXPANDED_LEAVE_ALONE = -10000;
public static final int EXPANDED_FULL_OPEN = -10001;
+ private static final int HIDDEN_NOTIFICATION_ID = 10000;
+ private static final String BANNER_ACTION_CANCEL =
+ "com.android.systemui.statusbar.banner_action_cancel";
+ private static final String BANNER_ACTION_SETUP =
+ "com.android.systemui.statusbar.banner_action_setup";
+
protected CommandQueue mCommandQueue;
protected IStatusBarService mBarService;
protected H mHandler = createHandler();
@@ -308,6 +320,20 @@
mUsersAllowingPrivateNotifications.clear();
updateLockscreenNotificationSetting();
updateNotifications();
+ } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
+ NotificationManager noMan = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ noMan.cancel(HIDDEN_NOTIFICATION_ID);
+
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
+ if (BANNER_ACTION_SETUP.equals(action)) {
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
+ mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
+ );
+ }
}
}
};
@@ -490,12 +516,61 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
+ filter.addAction(BANNER_ACTION_CANCEL);
+ filter.addAction(BANNER_ACTION_SETUP);
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, filter);
updateCurrentProfilesCache();
}
+ protected void notifyUserAboutHiddenNotifications() {
+ if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
+ Log.d(TAG, "user hasn't seen notification about hidden notifications");
+ final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
+ if (!lockPatternUtils.isSecure()) {
+ Log.d(TAG, "insecure lockscreen, skipping notification");
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
+ return;
+ }
+ Log.d(TAG, "disabling lockecreen notifications and alerting the user");
+ // disable lockscreen notifications until user acts on the banner.
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
+
+ final String packageName = mContext.getPackageName();
+ PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
+ PendingIntent.FLAG_CANCEL_CURRENT);
+
+ final Resources res = mContext.getResources();
+ final int colorRes = com.android.internal.R.color.system_notification_accent_color;
+ Notification.Builder note = new Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_android)
+ .setContentTitle(mContext.getString(R.string.hidden_notifications_title))
+ .setContentText(mContext.getString(R.string.hidden_notifications_text))
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setOngoing(true)
+ .setColor(res.getColor(colorRes))
+ .setContentIntent(setupIntent)
+ .addAction(R.drawable.ic_close,
+ mContext.getString(R.string.hidden_notifications_cancel),
+ cancelIntent)
+ .addAction(R.drawable.ic_settings,
+ mContext.getString(R.string.hidden_notifications_setup),
+ setupIntent);
+
+ NotificationManager noMan =
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ noMan.notify(HIDDEN_NOTIFICATION_ID, note.build());
+ }
+ }
+
public void userSwitched(int newUserId) {
// should be overridden
}
@@ -582,9 +657,11 @@
protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
NotificationData.Entry entry) {
+ PackageManager pmUser = getPackageManagerForUser(
+ entry.notification.getUser().getIdentifier());
int version = 0;
try {
- ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.getPackageName(), 0);
+ ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
version = info.targetSdkVersion;
} catch (NameNotFoundException ex) {
Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 62552b2..f9da30f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -380,21 +380,10 @@
mLockIcon.setImageResource(iconRes);
boolean trustManaged = mUnlockMethodCache.isTrustManaged();
mTrustDrawable.setTrustManaged(trustManaged);
-
updateLockIconClickability();
- updateLockIconContentDescription(mUnlockMethodCache.isFaceUnlockRunning(),
- mUnlockMethodCache.isMethodInsecure(), trustManaged);
}
- private void updateLockIconContentDescription(boolean faceUnlockRunning, boolean insecure,
- boolean trustManaged) {
- mLockIcon.setContentDescription(getResources().getString(
- faceUnlockRunning ? R.string.accessibility_unlock_button_face_unlock_running
- : insecure && !trustManaged ? R.string.accessibility_unlock_button_not_secured
- : insecure ? R.string.accessibility_unlock_button_not_secured_trust_managed
- : !trustManaged ? R.string.accessibility_unlock_button_secured
- : R.string.accessibility_unlock_button_secured_trust_managed));
- }
+
public KeyguardAffordanceView getPhoneView() {
return mPhoneImageView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 9fd3d9c..1a0d2d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -582,6 +582,8 @@
putComponent(PhoneStatusBar.class, this);
setControllerUsers();
+
+ notifyUserAboutHiddenNotifications();
}
// ================================================================================
@@ -3612,7 +3614,8 @@
}
public boolean onSpacePressed() {
- if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
+ if (mScreenOn != null && mScreenOn
+ && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
animateCollapsePanels(0 /* flags */, true /* force */);
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index f03c5eb..12c887e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -26,7 +26,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -46,7 +45,6 @@
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
-import android.provider.Settings.Global;
import android.util.Log;
import android.util.SparseArray;
import android.view.KeyEvent;
@@ -58,6 +56,7 @@
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import android.view.accessibility.AccessibilityManager;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
@@ -97,6 +96,7 @@
private static final int TIMEOUT_DELAY_SHORT = 1500;
private static final int TIMEOUT_DELAY_COLLAPSED = 4500;
private static final int TIMEOUT_DELAY_SAFETY_WARNING = 5000;
+ private static final int TIMEOUT_DELAY_SAFETY_WARNING_TALKBACK = 25000;
private static final int TIMEOUT_DELAY_EXPANDED = 10000;
private static final int MSG_VOLUME_CHANGED = 0;
@@ -161,6 +161,7 @@
private int mActiveStreamType = -1;
/** All the slider controls mapped by stream type */
private SparseArray<StreamControl> mStreamControls;
+ private final AccessibilityManager mAccessibilityManager;
private enum StreamResources {
BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
@@ -332,6 +333,8 @@
mContext = context;
mZenController = zenController;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mAccessibilityManager = (AccessibilityManager) context.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
// For now, only show master volume if master volume is supported
final Resources res = context.getResources();
@@ -791,7 +794,8 @@
}
private void updateTimeoutDelay() {
- mTimeoutDelay = sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
+ mTimeoutDelay = sSafetyWarning != null ? mAccessibilityManager.isEnabled() ?
+ TIMEOUT_DELAY_SAFETY_WARNING_TALKBACK : TIMEOUT_DELAY_SAFETY_WARNING
: mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
: mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
: isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
@@ -1214,6 +1218,7 @@
}
updateStates();
}
+ updateTimeoutDelay();
resetTimeout();
}
diff --git a/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java b/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java
index 71b0d53..6f79f58 100644
--- a/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java
+++ b/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java
@@ -83,6 +83,7 @@
private final Context mContext;
+ private final Runnable mOnAccessibilityEnabledCallback;
private final UserManager mUserManager;
private final TextToSpeech mTts;
private final Ringtone mTone;
@@ -97,8 +98,9 @@
private float mSecondPointerDownX;
private float mSecondPointerDownY;
- public EnableAccessibilityController(Context context) {
+ public EnableAccessibilityController(Context context, Runnable onAccessibilityEnabledCallback) {
mContext = context;
+ mOnAccessibilityEnabledCallback = onAccessibilityEnabledCallback;
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mTts = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
@Override
@@ -275,5 +277,7 @@
/* ignore */
}
}
+
+ mOnAccessibilityEnabledCallback.run();
}
}
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index ae94654..41695c1 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -1073,7 +1073,13 @@
// is dismissed on the first down while the global gesture is a long press
// with two fingers anywhere on the screen.
if (EnableAccessibilityController.canEnableAccessibilityViaGesture(mContext)) {
- mEnableAccessibilityController = new EnableAccessibilityController(mContext);
+ mEnableAccessibilityController = new EnableAccessibilityController(mContext,
+ new Runnable() {
+ @Override
+ public void run() {
+ dismiss();
+ }
+ });
super.setCanceledOnTouchOutside(false);
} else {
mEnableAccessibilityController = null;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index a13da609..93591a9 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -2754,11 +2754,13 @@
mStatusColorView = updateColorViewInt(mStatusColorView,
SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
mStatusBarColor, mLastTopInset, Gravity.TOP,
- STATUS_BAR_BACKGROUND_TRANSITION_NAME);
+ STATUS_BAR_BACKGROUND_TRANSITION_NAME,
+ com.android.internal.R.id.statusBarBackground);
mNavigationColorView = updateColorViewInt(mNavigationColorView,
SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
mNavigationBarColor, mLastBottomInset, Gravity.BOTTOM,
- NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME);
+ NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
+ com.android.internal.R.id.navigationBarBackground);
}
if (insets != null) {
insets = insets.consumeStableInsets();
@@ -2767,7 +2769,7 @@
}
private View updateColorViewInt(View view, int systemUiHideFlag, int translucentFlag,
- int color, int height, int verticalGravity, String transitionName) {
+ int color, int height, int verticalGravity, String transitionName, int id) {
boolean show = height > 0 && (mLastSystemUiVisibility & systemUiHideFlag) == 0
&& (getAttributes().flags & translucentFlag) == 0
&& (color & Color.BLACK) != 0
@@ -2778,6 +2780,7 @@
view = new View(mContext);
view.setBackgroundColor(color);
view.setTransitionName(transitionName);
+ view.setId(id);
addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, height,
Gravity.START | verticalGravity));
}
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index ac0ca0a..af5c13d 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -708,6 +708,7 @@
// Send an event to the end of the drag gesture.
sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
}
+ mCurrentState = STATE_TOUCH_EXPLORING;
} break;
case MotionEvent.ACTION_UP: {
mAms.onTouchInteractionEnd();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1367761..6310764 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8948,6 +8948,14 @@
return false;
}
+ private void checkTime(long startTime, String where) {
+ long now = SystemClock.elapsedRealtime();
+ if ((now-startTime) > 1000) {
+ // If we are taking more than a second, log about it.
+ Slog.w(TAG, "Slow operation: " + (now-startTime) + "ms so far, now at " + where);
+ }
+ }
+
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
@@ -8955,6 +8963,8 @@
ProviderInfo cpi = null;
synchronized(this) {
+ long startTime = SystemClock.elapsedRealtime();
+
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);
@@ -8968,6 +8978,8 @@
boolean checkCrossUser = true;
+ checkTime(startTime, "getContentProviderImpl: getProviderByName");
+
// First check if this content provider has been published...
cpr = mProviderMap.getProviderByName(name, userId);
// If that didn't work, check if it exists for user 0 and then
@@ -8992,10 +9004,12 @@
if (providerRunning) {
cpi = cpr.info;
String msg;
+ checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
!= null) {
throw new SecurityException(msg);
}
+ checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");
if (r != null && cpr.canRunHere(r)) {
// This provider has been published or is in the process
@@ -9011,6 +9025,8 @@
final long origId = Binder.clearCallingIdentity();
+ checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");
+
// In this case the provider instance already exists, so we can
// return it right away.
conn = incProviderCountLocked(r, cpr, token, stable);
@@ -9020,7 +9036,9 @@
// make sure to count it as being accessed and thus
// back up on the LRU list. This is good because
// content providers are often expensive to start.
+ checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
updateLruProcessLocked(cpr.proc, false, null);
+ checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
}
}
@@ -9033,7 +9051,9 @@
Process.killProcess(cpr.proc.pid);
}
}
+ checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
boolean success = updateOomAdjLocked(cpr.proc);
+ checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
if (DEBUG_PROVIDER) Slog.i(TAG, "Adjust success: " + success);
// NOTE: there is still a race here where a signal could be
// pending on the process even though we managed to update its
@@ -9048,7 +9068,9 @@
"Existing provider " + cpr.name.flattenToShortString()
+ " is crashing; detaching " + r);
boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
+ checkTime(startTime, "getContentProviderImpl: before appDied");
appDiedLocked(cpr.proc);
+ checkTime(startTime, "getContentProviderImpl: after appDied");
if (!lastRef) {
// This wasn't the last ref our process had on
// the provider... we have now been killed, bail.
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 1d2f7a9..6545134 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2072,7 +2072,7 @@
}
targetStack = inTask.stack;
targetStack.moveTaskToFrontLocked(inTask, r, options);
- mWindowManager.moveTaskToTop(targetStack.topTask().taskId);
+ mWindowManager.moveTaskToTop(inTask.taskId);
// Check whether we should actually launch the new activity in to the task,
// or just reuse the current activity on top.
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index ff22764..a37249d 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -166,8 +166,16 @@
}
if (full) {
if (hasExternalProcessHandles()) {
- pw.print(prefix); pw.print("externals=");
- pw.println(externalProcessTokenToHandle.size());
+ pw.print(prefix); pw.print("externals:");
+ if (externalProcessTokenToHandle != null) {
+ pw.print(" w/token=");
+ pw.print(externalProcessTokenToHandle.size());
+ }
+ if (externalProcessNoHandleCount > 0) {
+ pw.print(" notoken=");
+ pw.print(externalProcessNoHandleCount);
+ }
+ pw.println();
}
} else {
if (connections.size() > 0 || externalProcessNoHandleCount > 0) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 77c324f..1287dce 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -21,6 +21,7 @@
import java.nio.ByteBuffer;
import android.app.ActivityManager;
+import android.os.SystemClock;
import com.android.internal.util.MemInfoReader;
import com.android.server.wm.WindowManagerService;
@@ -528,12 +529,18 @@
if (amt == UNKNOWN_ADJ)
return;
+ long start = SystemClock.elapsedRealtime();
ByteBuffer buf = ByteBuffer.allocate(4 * 4);
buf.putInt(LMK_PROCPRIO);
buf.putInt(pid);
buf.putInt(uid);
buf.putInt(amt);
writeLmkd(buf);
+ long now = SystemClock.elapsedRealtime();
+ if ((now-start) > 250) {
+ Slog.w("ActivityManager", "SLOW OOM ADJ: " + (now-start) + "ms for pid " + pid
+ + " = " + amt);
+ }
}
/*
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 38077eb..8c342dd 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1009,7 +1009,7 @@
y[i] = normalizeAbsoluteBrightness(brightness[i]);
}
- Spline spline = Spline.createMonotoneCubicSpline(x, y);
+ Spline spline = Spline.createSpline(x, y);
if (DEBUG) {
Slog.d(TAG, "Auto-brightness spline: " + spline);
for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 827b3ed..bb22b4d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -23,6 +23,7 @@
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Predicate;
import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
@@ -30,6 +31,8 @@
import libcore.util.EmptyArray;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -446,7 +449,7 @@
allocated.add(address);
}
}
- mIoThreadLogger.debug("DevicePollingResult:" + allocated);
+ mIoThreadLogger.debug("[P]:Allocated Address=" + allocated);
if (callback != null) {
runOnServiceThread(new Runnable() {
@Override
@@ -548,7 +551,7 @@
runOnIoThread(new Runnable() {
@Override
public void run() {
- mIoThreadLogger.debug("SendCommand:" + cecMessage);
+ mIoThreadLogger.debug("[S]:" + cecMessage);
byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
int i = 0;
int errorCode = Constants.SEND_RESULT_SUCCESS;
@@ -583,7 +586,7 @@
private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
assertRunOnServiceThread();
HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
- mServiceThreadLogger.debug("ReceiveCommand:" + command);
+ mServiceThreadLogger.debug("[R]:" + command);
onReceiveCommand(command);
}
@@ -598,6 +601,15 @@
mService.onHotplug(port, connected);
}
+ void dump(final IndentingPrintWriter pw) {
+ for (int i = 0; i < mLocalDevices.size(); ++i) {
+ pw.println("HdmiCecLocalDevice #" + i + ":");
+ pw.increaseIndent();
+ mLocalDevices.valueAt(i).dump(pw);
+ pw.decreaseIndent();
+ }
+ }
+
private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
int dstAddress, byte[] body);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
index 26d2cde..85f5be2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
@@ -43,6 +43,9 @@
*/
abstract class HdmiCecFeatureAction {
private static final String TAG = "HdmiCecFeatureAction";
+ // As all actions run in the same thread (service thread), it's fine to have single logger.
+ // TODO: create global logger for each threads and use them.
+ protected static final HdmiLogger DLOGGER = new HdmiLogger(TAG);
// Timer handler message used for timeout event
protected static final int MSG_TIMEOUT = 100;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index a12e4fc..38addba 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -28,6 +28,7 @@
import android.view.KeyEvent;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import java.util.ArrayList;
@@ -97,6 +98,17 @@
public int hashCode() {
return logicalAddress * 29 + physicalAddress;
}
+ @Override
+ public String toString() {
+ StringBuffer s = new StringBuffer();
+ String logicalAddressString = (logicalAddress == Constants.ADDR_INVALID)
+ ? "invalid" : String.format("0x%02x", logicalAddress);
+ s.append("logical_address: ").append(logicalAddressString);
+ String physicalAddressString = (physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS)
+ ? "invalid" : String.format("0x%04x", physicalAddress);
+ s.append(", physical_address: ").append(physicalAddressString);
+ return s.toString();
+ }
}
// Logical address of the active source.
@GuardedBy("mLock")
@@ -793,4 +805,16 @@
protected void sendKeyEvent(int keyCode, boolean isPressed) {
Slog.w(TAG, "sendKeyEvent not implemented");
}
+
+ /**
+ * Dump internal status of HdmiCecLocalDevice object.
+ */
+ protected void dump(final IndentingPrintWriter pw) {
+ pw.println("mDeviceType: " + mDeviceType);
+ pw.println("mAddress: " + mAddress);
+ pw.println("mPreferredAddress: " + mPreferredAddress);
+ pw.println("mDeviceInfo: " + mDeviceInfo);
+ pw.println("mActiveSource: " + mActiveSource);
+ pw.println(String.format("mActiveRoutingPath: 0x%04x", mActiveRoutingPath));
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 5a2fa9c..6603a71 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -23,6 +23,7 @@
import android.os.SystemProperties;
import android.util.Slog;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
/**
@@ -219,4 +220,10 @@
mIsActiveSource = false;
checkIfPendingActionsCleared();
}
+
+ @Override
+ protected void dump(final IndentingPrintWriter pw) {
+ super.dump(pw);
+ pw.println("mIsActiveSource: " + mIsActiveSource);
+ }
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index cd56cfc..1ab8069 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -45,6 +45,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
@@ -1610,4 +1611,16 @@
invokeDeviceEventListener(newInfo, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
}
+
+ @Override
+ protected void dump(final IndentingPrintWriter pw) {
+ super.dump(pw);
+ pw.println("mArcEstablished: " + mArcEstablished);
+ pw.println("mArcFeatureEnabled: " + mArcFeatureEnabled);
+ pw.println("mSystemAudioActivated: " + mSystemAudioActivated);
+ pw.println("mSystemAudioMute: " + mSystemAudioMute);
+ pw.println("mAutoDeviceOff: " + mAutoDeviceOff);
+ pw.println("mAutoWakeup: " + mAutoWakeup);
+ pw.println("mSkipRoutingControl: " + mSkipRoutingControl);
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index e7b920a..d13e1de 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -67,6 +67,7 @@
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.SystemService;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
@@ -75,6 +76,8 @@
import libcore.util.EmptyArray;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -1395,6 +1398,28 @@
enforceAccessPermission();
HdmiControlService.this.addHdmiMhlScratchpadCommandListener(listener);
}
+
+ @Override
+ protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+ final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
+
+ pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
+ pw.println("mProhibitMode: " + mProhibitMode);
+ if (mCecController != null) {
+ pw.println("mCecController: ");
+ pw.increaseIndent();
+ mCecController.dump(pw);
+ pw.decreaseIndent();
+ }
+ pw.println("mPortInfo: ");
+ pw.increaseIndent();
+ for (HdmiPortInfo hdmiPortInfo : mPortInfo) {
+ pw.println("- " + hdmiPortInfo);
+ }
+ pw.decreaseIndent();
+ pw.println("mPowerStatus: " + mPowerStatus);
+ }
}
@ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiLogger.java b/services/core/java/com/android/server/hdmi/HdmiLogger.java
index ee9379d..c7add75 100644
--- a/services/core/java/com/android/server/hdmi/HdmiLogger.java
+++ b/services/core/java/com/android/server/hdmi/HdmiLogger.java
@@ -42,7 +42,7 @@
private final String mTag;
HdmiLogger(String tag) {
- mTag = tag;
+ mTag = "HDMI:" + tag;
}
void warning(String logMessage) {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index ac2c7b9..d15ffb0 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -73,9 +73,9 @@
// Seq #27
protected void sendSystemAudioModeRequest() {
- mState = STATE_CHECK_ROUTING_IN_PRGRESS;
List<RoutingControlAction> routingActions = getActions(RoutingControlAction.class);
if (!routingActions.isEmpty()) {
+ mState = STATE_CHECK_ROUTING_IN_PRGRESS;
// Should have only one Routing Control Action
RoutingControlAction routingAction = routingActions.get(0);
routingAction.addOnFinishedCallback(this, new Runnable() {
@@ -97,20 +97,21 @@
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
- if (error == Constants.SEND_RESULT_SUCCESS) {
- mState = STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE;
- addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS);
- } else {
+ if (error != Constants.SEND_RESULT_SUCCESS) {
+ DLOGGER.debug("Failed to send <System Audio Mode Request>:" + error);
setSystemAudioMode(false);
finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
}
}
});
+ mState = STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE;
+ addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS);
}
private void handleSendSystemAudioModeRequestTimeout() {
if (!mTargetAudioStatus // Don't retry for Off case.
|| mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) {
+ DLOGGER.debug("[T]:wait for <Set System Audio Mode>.");
setSystemAudioMode(false);
finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
return;
@@ -129,6 +130,7 @@
if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT
&& (cmd.getParams()[0] & 0xFF)
== Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST) {
+ DLOGGER.debug("Failed to start system audio mode request.");
setSystemAudioMode(false);
finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
return true;
@@ -143,6 +145,7 @@
startAudioStatusAction();
return true;
} else {
+ DLOGGER.debug("Unexpected system audio mode request:" + receivedStatus);
// Unexpected response, consider the request is newly initiated by AVR.
// To return 'false' will initiate new SystemAudioActionFromAvr by the control
// service.
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 379ec94..c3bc306 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -819,6 +819,7 @@
};
void dumpInternal(PrintWriter pw) {
+ final long now = SystemClock.elapsedRealtime();
synchronized (mJobs) {
pw.print("Started users: ");
for (int i=0; i<mStartedUsers.size(); i++) {
@@ -833,15 +834,14 @@
job.dump(pw, " ");
}
} else {
- pw.println();
- pw.println("No jobs scheduled.");
+ pw.println(" None.");
}
for (int i=0; i<mControllers.size(); i++) {
pw.println();
mControllers.get(i).dumpControllerState(pw);
}
pw.println();
- pw.println("Pending");
+ pw.println("Pending:");
for (int i=0; i<mPendingJobs.size(); i++) {
pw.println(mPendingJobs.get(i).hashCode());
}
@@ -852,10 +852,14 @@
if (jsc.isAvailable()) {
continue;
} else {
- pw.println(jsc.getRunningJob().hashCode() + " for: " +
- (SystemClock.elapsedRealtime()
- - jsc.getExecutionStartTimeElapsed())/1000 + "s " +
- "timeout: " + jsc.getTimeoutElapsed());
+ final long timeout = jsc.getTimeoutElapsed();
+ pw.print("Running for: ");
+ pw.print((now - jsc.getExecutionStartTimeElapsed())/1000);
+ pw.print("s timeout=");
+ pw.print(timeout);
+ pw.print(" fromnow=");
+ pw.println(timeout-now);
+ jsc.getRunningJob().dump(pw, " ");
}
}
pw.println();
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 6f5d3c2..f562721 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -251,6 +251,7 @@
// Dumpsys infrastructure
public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
pw.println(this.toString());
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index fc1b746..d0f4054 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -17,6 +17,8 @@
package com.android.server.notification;
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
+import static android.service.notification.NotificationListenerService.TRIM_FULL;
+import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -50,6 +52,7 @@
import android.media.IRingtonePlayer;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
@@ -1290,24 +1293,23 @@
*/
@Override
public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
- INotificationListener token, String[] keys) {
+ INotificationListener token, String[] keys, int trim) {
synchronized (mNotificationList) {
final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
- final ArrayList<StatusBarNotification> list
- = new ArrayList<StatusBarNotification>();
final boolean getKeys = keys != null;
final int N = getKeys ? keys.length : mNotificationList.size();
- list.ensureCapacity(N);
+ final ArrayList<StatusBarNotification> list
+ = new ArrayList<StatusBarNotification>(N);
for (int i=0; i<N; i++) {
final NotificationRecord r = getKeys
? mNotificationsByKey.get(keys[i])
: mNotificationList.get(i);
- if (r != null) {
- StatusBarNotification sbn = r.sbn;
- if (isVisibleToListener(sbn, info)) {
- list.add(sbn);
- }
- }
+ if (r == null) continue;
+ StatusBarNotification sbn = r.sbn;
+ if (!isVisibleToListener(sbn, info)) continue;
+ StatusBarNotification sbnToSend =
+ (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
+ list.add(sbnToSend);
}
return new ParceledListSlice<StatusBarNotification>(list);
}
@@ -1364,6 +1366,16 @@
}
@Override
+ public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
+ throws RemoteException {
+ synchronized (mNotificationList) {
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ if (info == null) return;
+ mListeners.setOnNotificationPostedTrimLocked(info, trim);
+ }
+ }
+
+ @Override
public ZenModeConfig getZenModeConfig() {
enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
return mZenModeHelper.getConfig();
@@ -1427,7 +1439,7 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump NotificationManager from from pid="
+ pw.println("Permission Denial: can't dump NotificationManager from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
return;
@@ -1441,6 +1453,13 @@
enforceSystemOrSystemUI("INotificationManager.getEffectsSuppressor");
return mEffectsSuppressor;
}
+
+ @Override
+ public boolean matchesCallFilter(Bundle extras) {
+ enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
+ return mZenModeHelper.matchesCallFilter(extras,
+ mRankingHelper.findExtractor(ValidateNotificationPeople.class));
+ }
};
private String[] getActiveNotificationKeys(INotificationListener token) {
@@ -2611,6 +2630,8 @@
public class NotificationListeners extends ManagedServices {
+ private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
+
public NotificationListeners() {
super(getContext(), mHandler, mNotificationList, mUserProfiles);
}
@@ -2651,6 +2672,20 @@
if (mListenersDisablingEffects.remove(removed)) {
updateListenerHintsLocked();
}
+ mLightTrimListeners.remove(removed);
+ }
+
+ public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
+ if (trim == TRIM_LIGHT) {
+ mLightTrimListeners.add(info);
+ } else {
+ mLightTrimListeners.remove(info);
+ }
+ }
+
+ public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
+ return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
+
}
/**
@@ -2661,8 +2696,10 @@
* but isn't anymore.
*/
public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
- // make a copy in case changes are made to the underlying Notification object
- final StatusBarNotification sbnClone = sbn.clone();
+ // Lazily initialized snapshots of the notification.
+ StatusBarNotification sbnClone = null;
+ StatusBarNotification sbnCloneLight = null;
+
for (final ManagedServiceInfo info : mServices) {
boolean sbnVisible = isVisibleToListener(sbn, info);
boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
@@ -2684,10 +2721,20 @@
continue;
}
+ final int trim = mListeners.getOnNotificationPostedTrim(info);
+
+ if (trim == TRIM_LIGHT && sbnCloneLight == null) {
+ sbnCloneLight = sbn.cloneLight();
+ } else if (trim == TRIM_FULL && sbnClone == null) {
+ sbnClone = sbn.clone();
+ }
+ final StatusBarNotification sbnToPost =
+ (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;
+
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyPosted(info, sbnClone, update);
+ notifyPosted(info, sbnToPost, update);
}
});
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 01188af..435177b 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -87,6 +87,17 @@
mProxyByGroupTmp = new ArrayMap<String, NotificationRecord>();
}
+ public <T extends NotificationSignalExtractor> T findExtractor(Class<T> extractorClass) {
+ final int N = mSignalExtractors.length;
+ for (int i = 0; i < N; i++) {
+ final NotificationSignalExtractor extractor = mSignalExtractors[i];
+ if (extractorClass.equals(extractor.getClass())) {
+ return (T) extractor;
+ }
+ }
+ return null;
+ }
+
public void extractSignals(NotificationRecord r) {
final int N = mSignalExtractors.length;
for (int i = 0; i < N; i++) {
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index bdc364c..aa47858 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -71,8 +71,17 @@
private LruCache<String, LookupResult> mPeopleCache;
private RankingReconsideration validatePeople(final NotificationRecord record) {
+ final String key = record.getKey();
+ final Bundle extras = record.getNotification().extras;
+ final float[] affinityOut = new float[1];
+ final RankingReconsideration rr = validatePeople(key, extras, affinityOut);
+ record.setContactAffinity(affinityOut[0]);
+ return rr;
+ }
+
+ private PeopleRankingReconsideration validatePeople(String key, Bundle extras,
+ float[] affinityOut) {
float affinity = NONE;
- Bundle extras = record.getNotification().extras;
if (extras == null) {
return null;
}
@@ -82,7 +91,7 @@
return null;
}
- if (INFO) Slog.i(TAG, "Validating: " + record.sbn.getKey());
+ if (INFO) Slog.i(TAG, "Validating: " + key);
final LinkedList<String> pendingLookups = new LinkedList<String>();
for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) {
final String handle = people[personIdx];
@@ -102,51 +111,15 @@
}
// record the best available data, so far:
- record.setContactAffinity(affinity);
+ affinityOut[0] = affinity;
if (pendingLookups.isEmpty()) {
if (INFO) Slog.i(TAG, "final affinity: " + affinity);
return null;
}
- if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + record.sbn.getKey());
- return new RankingReconsideration(record.getKey()) {
- float mContactAffinity = NONE;
- @Override
- public void work() {
- if (INFO) Slog.i(TAG, "Executing: validation for: " + record.getKey());
- for (final String handle: pendingLookups) {
- LookupResult lookupResult = null;
- final Uri uri = Uri.parse(handle);
- if ("tel".equals(uri.getScheme())) {
- if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
- lookupResult = resolvePhoneContact(uri.getSchemeSpecificPart());
- } else if ("mailto".equals(uri.getScheme())) {
- if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
- lookupResult = resolveEmailContact(uri.getSchemeSpecificPart());
- } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
- if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
- lookupResult = searchContacts(uri);
- } else {
- lookupResult = new LookupResult(); // invalid person for the cache
- Slog.w(TAG, "unsupported URI " + handle);
- }
- if (lookupResult != null) {
- synchronized (mPeopleCache) {
- mPeopleCache.put(handle, lookupResult);
- }
- mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
- }
- }
- }
-
- @Override
- public void applyChangesLocked(NotificationRecord operand) {
- float affinityBound = operand.getContactAffinity();
- operand.setContactAffinity(Math.max(mContactAffinity, affinityBound));
- if (INFO) Slog.i(TAG, "final affinity: " + operand.getContactAffinity());
- }
- };
+ if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + key);
+ return new PeopleRankingReconsideration(key, pendingLookups);
}
// VisibleForTesting
@@ -269,6 +242,19 @@
// ignore: config has no relevant information yet.
}
+ public float getContactAffinity(Bundle extras) {
+ if (extras == null) return NONE;
+ final String key = Long.toString(System.nanoTime());
+ final float[] affinityOut = new float[1];
+ final PeopleRankingReconsideration prr = validatePeople(key, extras, affinityOut);
+ float affinity = affinityOut[0];
+ if (prr != null) {
+ prr.work();
+ affinity = Math.max(prr.getContactAffinity(), affinity);
+ }
+ return affinity;
+ }
+
private static class LookupResult {
private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr
public static final int INVALID_ID = -1;
@@ -328,5 +314,55 @@
return this;
}
}
+
+ private class PeopleRankingReconsideration extends RankingReconsideration {
+ private final LinkedList<String> mPendingLookups;
+
+ private float mContactAffinity = NONE;
+
+ private PeopleRankingReconsideration(String key, LinkedList<String> pendingLookups) {
+ super(key);
+ mPendingLookups = pendingLookups;
+ }
+
+ @Override
+ public void work() {
+ if (INFO) Slog.i(TAG, "Executing: validation for: " + mKey);
+ for (final String handle: mPendingLookups) {
+ LookupResult lookupResult = null;
+ final Uri uri = Uri.parse(handle);
+ if ("tel".equals(uri.getScheme())) {
+ if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
+ lookupResult = resolvePhoneContact(uri.getSchemeSpecificPart());
+ } else if ("mailto".equals(uri.getScheme())) {
+ if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
+ lookupResult = resolveEmailContact(uri.getSchemeSpecificPart());
+ } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
+ if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
+ lookupResult = searchContacts(uri);
+ } else {
+ lookupResult = new LookupResult(); // invalid person for the cache
+ Slog.w(TAG, "unsupported URI " + handle);
+ }
+ if (lookupResult != null) {
+ synchronized (mPeopleCache) {
+ mPeopleCache.put(handle, lookupResult);
+ }
+ mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
+ }
+ }
+ }
+
+ @Override
+ public void applyChangesLocked(NotificationRecord operand) {
+ float affinityBound = operand.getContactAffinity();
+ operand.setContactAffinity(Math.max(mContactAffinity, affinityBound));
+ if (INFO) Slog.i(TAG, "final affinity: " + operand.getContactAffinity());
+ }
+
+ public float getContactAffinity() {
+ return mContactAffinity;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 7a5336b..fd35ede 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -34,6 +34,7 @@
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings.Global;
@@ -189,7 +190,7 @@
}
private boolean shouldInterceptAudience(NotificationRecord record) {
- if (!audienceMatches(record)) {
+ if (!audienceMatches(record.getContactAffinity())) {
ZenLog.traceIntercepted(record, "!audienceMatches");
return true;
}
@@ -372,14 +373,27 @@
return record.isCategory(Notification.CATEGORY_MESSAGE) || isDefaultMessagingApp(record);
}
- private boolean audienceMatches(NotificationRecord record) {
+ public boolean matchesCallFilter(Bundle extras, ValidateNotificationPeople validator) {
+ final int zen = mZenMode;
+ if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through
+ if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
+ if (!mConfig.allowCalls) return false; // no calls get through
+ if (validator != null) {
+ final float contactAffinity = validator.getContactAffinity(extras);
+ return audienceMatches(contactAffinity);
+ }
+ }
+ return true;
+ }
+
+ private boolean audienceMatches(float contactAffinity) {
switch (mConfig.allowFrom) {
case ZenModeConfig.SOURCE_ANYONE:
return true;
case ZenModeConfig.SOURCE_CONTACT:
- return record.getContactAffinity() >= ValidateNotificationPeople.VALID_CONTACT;
+ return contactAffinity >= ValidateNotificationPeople.VALID_CONTACT;
case ZenModeConfig.SOURCE_STAR:
- return record.getContactAffinity() >= ValidateNotificationPeople.STARRED_CONTACT;
+ return contactAffinity >= ValidateNotificationPeople.STARRED_CONTACT;
default:
Slog.w(TAG, "Encountered unknown source: " + mConfig.allowFrom);
return true;
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 694669c..ca11862 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -224,10 +224,6 @@
}
}
- public int pruneDexCache(String cacheSubDir) {
- return mInstaller.execute("prunedexcache " + cacheSubDir);
- }
-
public int freeCache(long freeStorageSize) {
StringBuilder builder = new StringBuilder("freecache");
builder.append(' ');
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 89878ef..fbaeb37 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1497,29 +1497,6 @@
}
}
- if (didDexOptLibraryOrTool) {
- // If we dexopted a library or tool, then something on the system has
- // changed. Consider this significant, and wipe away all other
- // existing dexopt files to ensure we don't leave any dangling around.
- //
- // TODO: This should be revisited because it isn't as good an indicator
- // as it used to be. It used to include the boot classpath but at some point
- // DexFile.isDexOptNeeded started returning false for the boot
- // class path files in all cases. It is very possible in a
- // small maintenance release update that the library and tool
- // jars may be unchanged but APK could be removed resulting in
- // unused dalvik-cache files.
- for (String dexCodeInstructionSet : dexCodeInstructionSets) {
- mInstaller.pruneDexCache(dexCodeInstructionSet);
- }
-
- // Additionally, delete all dex files from the root directory
- // since there shouldn't be any there anyway, unless we're upgrading
- // from an older OS version or a build that contained the "old" style
- // flat scheme.
- mInstaller.pruneDexCache(".");
- }
-
// Collect vendor overlay packages.
// (Do this before scanning any apps.)
// For security and version matching reason, only consider
@@ -7721,8 +7698,21 @@
public void installPackage(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride) {
+ installPackageAsUser(originPath, observer, installFlags, installerPackageName, verificationParams,
+ packageAbiOverride, UserHandle.getCallingUserId());
+ }
+
+ @Override
+ public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
+ int installFlags, String installerPackageName, VerificationParams verificationParams,
+ String packageAbiOverride, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
null);
+ if (UserHandle.getCallingUserId() != userId) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "installPackage " + userId);
+ }
final File originFile = new File(originPath);
final int uid = Binder.getCallingUid();
@@ -7740,7 +7730,7 @@
if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
} else {
- user = new UserHandle(UserHandle.getUserId(uid));
+ user = new UserHandle(userId);
}
final int filteredInstallFlags;
@@ -12988,7 +12978,7 @@
}
/** Called by UserManagerService */
- void cleanUpUserLILPw(int userHandle) {
+ void cleanUpUserLILPw(UserManagerService userManager, int userHandle) {
mDirtyUsers.remove(userHandle);
mSettings.removeUserLPw(userHandle);
mPendingBroadcasts.remove(userHandle);
@@ -12999,6 +12989,50 @@
mInstaller.removeUserDataDirs(userHandle);
}
mUserNeedsBadging.delete(userHandle);
+ removeUnusedPackagesLILPw(userManager, userHandle);
+ }
+
+ /**
+ * We're removing userHandle and would like to remove any downloaded packages
+ * that are no longer in use by any other user.
+ * @param userHandle the user being removed
+ */
+ private void removeUnusedPackagesLILPw(UserManagerService userManager, final int userHandle) {
+ final boolean DEBUG_CLEAN_APKS = false;
+ int [] users = userManager.getUserIdsLPr();
+ Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
+ while (psit.hasNext()) {
+ PackageSetting ps = psit.next();
+ final String packageName = ps.pkg.packageName;
+ // Skip over if system app
+ if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ continue;
+ }
+ if (DEBUG_CLEAN_APKS) {
+ Slog.i(TAG, "Checking package " + packageName);
+ }
+ boolean keep = false;
+ for (int i = 0; i < users.length; i++) {
+ if (users[i] != userHandle && ps.getInstalled(users[i])) {
+ keep = true;
+ if (DEBUG_CLEAN_APKS) {
+ Slog.i(TAG, " Keeping package " + packageName + " for user "
+ + users[i]);
+ }
+ break;
+ }
+ }
+ if (!keep) {
+ if (DEBUG_CLEAN_APKS) {
+ Slog.i(TAG, " Removing package " + packageName);
+ }
+ mHandler.post(new Runnable() {
+ public void run() {
+ deletePackageX(packageName, userHandle, 0);
+ } //end run
+ });
+ }
+ }
}
/** Called by UserManagerService */
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 8ded7ca..2929939 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -293,6 +293,10 @@
private List<UserInfo> getProfilesLocked(int userId, boolean enabledOnly) {
UserInfo user = getUserInfoLocked(userId);
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+ if (user == null) {
+ // Probably a dying user
+ return users;
+ }
for (int i = 0; i < mUsers.size(); i++) {
UserInfo profile = mUsers.valueAt(i);
if (!isProfileOf(user, profile)) {
@@ -1280,7 +1284,7 @@
private void removeUserStateLocked(final int userHandle) {
// Cleanup package manager settings
- mPm.cleanUpUserLILPw(userHandle);
+ mPm.cleanUpUserLILPw(this, userHandle);
// Remove this user from the list
mUsers.remove(userHandle);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index a49d8ab..d22912c 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -119,7 +119,7 @@
mContext = context;
mContentResolver = context.getContentResolver();
- mWatchLogHandler = new WatchLogHandler(IoThread.get().getLooper());
+ mWatchLogHandler = new WatchLogHandler(mContentResolver, IoThread.get().getLooper());
mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
@@ -248,7 +248,7 @@
serviceState = new ServiceState(component, userId);
userState.serviceStateMap.put(component, serviceState);
} else {
- inputList.addAll(serviceState.mInputList);
+ inputList.addAll(serviceState.inputList);
}
} else {
try {
@@ -258,9 +258,6 @@
continue;
}
}
-
- // Reconnect the service if existing input is updated.
- updateServiceConnectionLocked(component, userId);
userState.packageSet.add(si.packageName);
}
@@ -273,7 +270,7 @@
if (state == null) {
state = new TvInputState();
}
- state.mInfo = info;
+ state.info = info;
inputMap.put(info.getId(), state);
}
@@ -285,7 +282,7 @@
for (String inputId : userState.inputMap.keySet()) {
if (!inputMap.containsKey(inputId)) {
- TvInputInfo info = userState.inputMap.get(inputId).mInfo;
+ TvInputInfo info = userState.inputMap.get(inputId).info;
ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
if (serviceState != null) {
abortPendingCreateSessionRequestsLocked(serviceState, inputId, userId);
@@ -352,9 +349,9 @@
}
// Release created sessions.
for (SessionState state : userState.sessionStateMap.values()) {
- if (state.mSession != null) {
+ if (state.session != null) {
try {
- state.mSession.release();
+ state.session.release();
} catch (RemoteException e) {
Slog.e(TAG, "error in release", e);
}
@@ -364,14 +361,14 @@
// Unregister all callbacks and unbind all services.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
- if (serviceState.mCallback != null) {
+ if (serviceState.callback != null) {
try {
- serviceState.mService.unregisterCallback(serviceState.mCallback);
+ serviceState.service.unregisterCallback(serviceState.callback);
} catch (RemoteException e) {
Slog.e(TAG, "error in unregisterCallback", e);
}
}
- mContext.unbindService(serviceState.mConnection);
+ mContext.unbindService(serviceState.connection);
}
userState.serviceStateMap.clear();
@@ -412,7 +409,7 @@
throw new IllegalArgumentException("Session state not found for token " + sessionToken);
}
// Only the application that requested this session or the system can access it.
- if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) {
+ if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) {
throw new SecurityException("Illegal access to the session with token " + sessionToken
+ " from uid " + callingUid);
}
@@ -424,10 +421,10 @@
}
private ITvInputSession getSessionLocked(SessionState sessionState) {
- ITvInputSession session = sessionState.mSession;
+ ITvInputSession session = sessionState.session;
if (session == null) {
throw new IllegalStateException("Session not yet created for token "
- + sessionState.mSessionToken);
+ + sessionState.sessionToken);
}
return session;
}
@@ -439,8 +436,8 @@
}
private static boolean shouldMaintainConnection(ServiceState serviceState) {
- return !serviceState.mSessionTokens.isEmpty() || serviceState.mIsHardware;
- // TODO: Find a way to maintain connection only when necessary.
+ return !serviceState.sessionTokens.isEmpty() || serviceState.isHardware;
+ // TODO: Find a way to maintain connection to hardware TV input service only when necessary.
}
private void updateServiceConnectionLocked(ComponentName component, int userId) {
@@ -449,18 +446,18 @@
if (serviceState == null) {
return;
}
- if (serviceState.mReconnecting) {
- if (!serviceState.mSessionTokens.isEmpty()) {
+ if (serviceState.reconnecting) {
+ if (!serviceState.sessionTokens.isEmpty()) {
// wait until all the sessions are removed.
return;
}
- serviceState.mReconnecting = false;
+ serviceState.reconnecting = false;
}
boolean maintainConnection = shouldMaintainConnection(serviceState);
- if (serviceState.mService == null && maintainConnection && userId == mCurrentUserId) {
+ if (serviceState.service == null && maintainConnection && userId == mCurrentUserId) {
// This means that the service is not yet connected but its state indicates that we
// have pending requests. Then, connect the service.
- if (serviceState.mBound) {
+ if (serviceState.bound) {
// We have already bound to the service so we don't try to bind again until after we
// unbind later on.
return;
@@ -470,18 +467,15 @@
}
Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
- // Binding service may fail if the service is updating.
- // In that case, the connection will be revived in buildTvInputListLocked called by
- // onSomePackagesChanged.
- serviceState.mBound = mContext.bindServiceAsUser(
- i, serviceState.mConnection, Context.BIND_AUTO_CREATE, new UserHandle(userId));
- } else if (serviceState.mService != null && !maintainConnection) {
+ serviceState.bound = mContext.bindServiceAsUser(
+ i, serviceState.connection, Context.BIND_AUTO_CREATE, new UserHandle(userId));
+ } else if (serviceState.service != null && !maintainConnection) {
// This means that the service is already connected but its state indicates that we have
// nothing to do with it. Then, disconnect the service.
if (DEBUG) {
Slog.d(TAG, "unbindService(service=" + component + ")");
}
- mContext.unbindService(serviceState.mConnection);
+ mContext.unbindService(serviceState.connection);
userState.serviceStateMap.remove(component);
}
}
@@ -491,19 +485,19 @@
// Let clients know the create session requests are failed.
UserState userState = getUserStateLocked(userId);
List<SessionState> sessionsToAbort = new ArrayList<>();
- for (IBinder sessionToken : serviceState.mSessionTokens) {
+ for (IBinder sessionToken : serviceState.sessionTokens) {
SessionState sessionState = userState.sessionStateMap.get(sessionToken);
- if (sessionState.mSession == null && (inputId == null
- || sessionState.mInfo.getId().equals(inputId))) {
+ if (sessionState.session == null && (inputId == null
+ || sessionState.info.getId().equals(inputId))) {
sessionsToAbort.add(sessionState);
}
}
for (SessionState sessionState : sessionsToAbort) {
- removeSessionStateLocked(sessionState.mSessionToken, sessionState.mUserId);
- sendSessionTokenToClientLocked(sessionState.mClient,
- sessionState.mInfo.getId(), null, null, sessionState.mSeq);
+ removeSessionStateLocked(sessionState.sessionToken, sessionState.userId);
+ sendSessionTokenToClientLocked(sessionState.client,
+ sessionState.info.getId(), null, null, sessionState.seq);
}
- updateServiceConnectionLocked(serviceState.mComponent, userId);
+ updateServiceConnectionLocked(serviceState.component, userId);
}
private ClientState createClientStateLocked(IBinder clientToken, int userId) {
@@ -518,221 +512,26 @@
return clientState;
}
- private void createSessionInternalLocked(ITvInputService service, final IBinder sessionToken,
- final int userId) {
- final UserState userState = getUserStateLocked(userId);
- final SessionState sessionState = userState.sessionStateMap.get(sessionToken);
+ private void createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
+ int userId) {
+ UserState userState = getUserStateLocked(userId);
+ SessionState sessionState = userState.sessionStateMap.get(sessionToken);
if (DEBUG) {
- Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInfo.getId() + ")");
+ Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.info.getId() + ")");
}
-
- final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
+ InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
// Set up a callback to send the session token.
- ITvInputSessionCallback callback = new ITvInputSessionCallback.Stub() {
- @Override
- public void onSessionCreated(ITvInputSession session, IBinder harewareSessionToken) {
- if (DEBUG) {
- Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInfo.getId() + ")");
- }
- synchronized (mLock) {
- sessionState.mSession = session;
- sessionState.mHardwareSessionToken = harewareSessionToken;
- if (session == null) {
- removeSessionStateLocked(sessionToken, userId);
- sendSessionTokenToClientLocked(sessionState.mClient,
- sessionState.mInfo.getId(), null, null, sessionState.mSeq);
- } else {
- try {
- session.asBinder().linkToDeath(sessionState, 0);
- } catch (RemoteException e) {
- Slog.e(TAG, "session process has already died", e);
- }
-
- IBinder clientToken = sessionState.mClient.asBinder();
- ClientState clientState = userState.clientStateMap.get(clientToken);
- if (clientState == null) {
- clientState = createClientStateLocked(clientToken, userId);
- }
- clientState.mSessionTokens.add(sessionState.mSessionToken);
-
- sendSessionTokenToClientLocked(sessionState.mClient,
- sessionState.mInfo.getId(), sessionToken, channels[0],
- sessionState.mSeq);
- }
- channels[0].dispose();
- }
- }
-
- @Override
- public void onChannelRetuned(Uri channelUri) {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- // TODO: Consider adding this channel change in the watch log. When we do
- // that, how we can protect the watch log from malicious tv inputs should
- // be addressed. e.g. add a field which represents where the channel change
- // originated from.
- sessionState.mClient.onChannelRetuned(channelUri, sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onChannelRetuned", e);
- }
- }
- }
-
- @Override
- public void onTracksChanged(List<TvTrackInfo> tracks) {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onTracksChanged(" + tracks + ")");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- sessionState.mClient.onTracksChanged(tracks, sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onTracksChanged", e);
- }
- }
- }
-
- @Override
- public void onTrackSelected(int type, String trackId) {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- sessionState.mClient.onTrackSelected(type, trackId, sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onTrackSelected", e);
- }
- }
- }
-
- @Override
- public void onVideoAvailable() {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onVideoAvailable()");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- sessionState.mClient.onVideoAvailable(sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onVideoAvailable", e);
- }
- }
- }
-
- @Override
- public void onVideoUnavailable(int reason) {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- sessionState.mClient.onVideoUnavailable(reason, sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onVideoUnavailable", e);
- }
- }
- }
-
- @Override
- public void onContentAllowed() {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onContentAllowed()");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- sessionState.mClient.onContentAllowed(sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onContentAllowed", e);
- }
- }
- }
-
- @Override
- public void onContentBlocked(String rating) {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onContentBlocked()");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- sessionState.mClient.onContentBlocked(rating, sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onContentBlocked", e);
- }
- }
- }
-
- @Override
- public void onLayoutSurface(int left, int top, int right, int bottom) {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
- + ", right=" + right + ", bottom=" + bottom + ",)");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- sessionState.mClient.onLayoutSurface(left, top, right, bottom,
- sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onLayoutSurface", e);
- }
- }
- }
-
- @Override
- public void onSessionEvent(String eventType, Bundle eventArgs) {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- sessionState.mClient.onSessionEvent(eventType, eventArgs,
- sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onSessionEvent", e);
- }
- }
- }
- };
+ ITvInputSessionCallback callback = new SessionCallback(sessionState, channels);
// Create a session. When failed, send a null token immediately.
try {
- service.createSession(channels[1], callback, sessionState.mInfo.getId());
+ service.createSession(channels[1], callback, sessionState.info.getId());
} catch (RemoteException e) {
Slog.e(TAG, "error in createSession", e);
removeSessionStateLocked(sessionToken, userId);
- sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInfo.getId(), null,
- null, sessionState.mSeq);
+ sendSessionTokenToClientLocked(sessionState.client, sessionState.info.getId(), null,
+ null, sessionState.seq);
}
channels[1].dispose();
}
@@ -748,17 +547,17 @@
private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
- if (sessionState.mSession != null) {
+ if (sessionState.session != null) {
UserState userState = getUserStateLocked(userId);
if (sessionToken == userState.mainSessionToken) {
setMainLocked(sessionToken, false, callingUid, userId);
}
try {
- sessionState.mSession.release();
+ sessionState.session.release();
} catch (RemoteException e) {
Slog.e(TAG, "session process has already died", e);
}
- sessionState.mSession = null;
+ sessionState.session = null;
}
removeSessionStateLocked(sessionToken, userId);
}
@@ -781,22 +580,22 @@
// Also remove the session token from the session token list of the current client and
// service.
- ClientState clientState = userState.clientStateMap.get(sessionState.mClient.asBinder());
+ ClientState clientState = userState.clientStateMap.get(sessionState.client.asBinder());
if (clientState != null) {
- clientState.mSessionTokens.remove(sessionToken);
+ clientState.sessionTokens.remove(sessionToken);
if (clientState.isEmpty()) {
- userState.clientStateMap.remove(sessionState.mClient.asBinder());
+ userState.clientStateMap.remove(sessionState.client.asBinder());
}
}
- TvInputInfo info = sessionState.mInfo;
+ TvInputInfo info = sessionState.info;
if (info != null) {
ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
if (serviceState != null) {
- serviceState.mSessionTokens.remove(sessionToken);
+ serviceState.sessionTokens.remove(sessionToken);
}
}
- updateServiceConnectionLocked(sessionState.mInfo.getComponent(), userId);
+ updateServiceConnectionLocked(sessionState.info.getComponent(), userId);
// Log the end of watch.
SomeArgs args = SomeArgs.obtain();
@@ -807,13 +606,12 @@
private void setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId) {
SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
- if (sessionState.mHardwareSessionToken != null) {
- sessionState = getSessionStateLocked(sessionState.mHardwareSessionToken,
+ if (sessionState.hardwareSessionToken != null) {
+ sessionState = getSessionStateLocked(sessionState.hardwareSessionToken,
Process.SYSTEM_UID, userId);
}
- ServiceState serviceState = getServiceStateLocked(sessionState.mInfo.getComponent(),
- userId);
- if (!serviceState.mIsHardware) {
+ ServiceState serviceState = getServiceStateLocked(sessionState.info.getComponent(), userId);
+ if (!serviceState.isHardware) {
return;
}
ITvInputSession session = getSessionLocked(sessionState);
@@ -876,10 +674,10 @@
private void setStateLocked(String inputId, int state, int userId) {
UserState userState = getUserStateLocked(userId);
TvInputState inputState = userState.inputMap.get(inputId);
- ServiceState serviceState = userState.serviceStateMap.get(inputState.mInfo.getComponent());
- int oldState = inputState.mState;
- inputState.mState = state;
- if (serviceState != null && serviceState.mService == null
+ ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent());
+ int oldState = inputState.state;
+ inputState.state = state;
+ if (serviceState != null && serviceState.service == null
&& shouldMaintainConnection(serviceState)) {
// We don't notify state change while reconnecting. It should remain disconnected.
return;
@@ -900,7 +698,7 @@
UserState userState = getUserStateLocked(resolvedUserId);
List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
for (TvInputState state : userState.inputMap.values()) {
- inputList.add(state.mInfo);
+ inputList.add(state.info);
}
return inputList;
}
@@ -918,7 +716,7 @@
synchronized (mLock) {
UserState userState = getUserStateLocked(resolvedUserId);
TvInputState state = userState.inputMap.get(inputId);
- return state == null ? null : state.mInfo;
+ return state == null ? null : state.info;
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -964,8 +762,8 @@
Slog.e(TAG, "client process has already died", e);
}
for (TvInputState state : userState.inputMap.values()) {
- notifyInputStateChangedLocked(userState, state.mInfo.getId(),
- state.mState, callback);
+ notifyInputStateChangedLocked(userState, state.info.getId(), state.state,
+ callback);
}
}
} finally {
@@ -1114,14 +912,14 @@
sendSessionTokenToClientLocked(client, inputId, null, null, seq);
return;
}
- TvInputInfo info = inputState.mInfo;
+ TvInputInfo info = inputState.info;
ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
if (serviceState == null) {
serviceState = new ServiceState(info.getComponent(), resolvedUserId);
userState.serviceStateMap.put(info.getComponent(), serviceState);
}
// Send a null token immediately while reconnecting.
- if (serviceState.mReconnecting == true) {
+ if (serviceState.reconnecting == true) {
sendSessionTokenToClientLocked(client, inputId, null, null, seq);
return;
}
@@ -1135,10 +933,10 @@
userState.sessionStateMap.put(sessionToken, sessionState);
// Also, add them to the session state map of the current service.
- serviceState.mSessionTokens.add(sessionToken);
+ serviceState.sessionTokens.add(sessionToken);
- if (serviceState.mService != null) {
- createSessionInternalLocked(serviceState.mService, sessionToken,
+ if (serviceState.service != null) {
+ createSessionInternalLocked(serviceState.service, sessionToken,
resolvedUserId);
} else {
updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
@@ -1213,10 +1011,10 @@
try {
SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
resolvedUserId);
- if (sessionState.mHardwareSessionToken == null) {
+ if (sessionState.hardwareSessionToken == null) {
getSessionLocked(sessionState).setSurface(surface);
} else {
- getSessionLocked(sessionState.mHardwareSessionToken,
+ getSessionLocked(sessionState.hardwareSessionToken,
Process.SYSTEM_UID, resolvedUserId).setSurface(surface);
}
} catch (RemoteException e) {
@@ -1244,9 +1042,10 @@
try {
SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
resolvedUserId);
- getSessionLocked(sessionState).dispatchSurfaceChanged(format, width, height);
- if (sessionState.mHardwareSessionToken != null) {
- getSessionLocked(sessionState.mHardwareSessionToken, Process.SYSTEM_UID,
+ getSessionLocked(sessionState).dispatchSurfaceChanged(format, width,
+ height);
+ if (sessionState.hardwareSessionToken != null) {
+ getSessionLocked(sessionState.hardwareSessionToken, Process.SYSTEM_UID,
resolvedUserId).dispatchSurfaceChanged(format, width, height);
}
} catch (RemoteException e) {
@@ -1272,10 +1071,10 @@
SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
resolvedUserId);
getSessionLocked(sessionState).setVolume(volume);
- if (sessionState.mHardwareSessionToken != null) {
+ if (sessionState.hardwareSessionToken != null) {
// Here, we let the hardware session know only whether volume is on or
// off to prevent that the volume is controlled in the both side.
- getSessionLocked(sessionState.mHardwareSessionToken,
+ getSessionLocked(sessionState.hardwareSessionToken,
Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f)
? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF);
}
@@ -1309,7 +1108,7 @@
// Log the start of watch.
SomeArgs args = SomeArgs.obtain();
- args.arg1 = sessionState.mInfo.getComponent().getPackageName();
+ args.arg1 = sessionState.info.getComponent().getPackageName();
args.arg2 = System.currentTimeMillis();
args.arg3 = ContentUris.parseId(channelUri);
args.arg4 = params;
@@ -1569,10 +1368,10 @@
return false;
}
for (SessionState sessionState : userState.sessionStateMap.values()) {
- if (sessionState.mInfo.getId().equals(inputId)
- && sessionState.mHardwareSessionToken != null) {
+ if (sessionState.info.getId().equals(inputId)
+ && sessionState.hardwareSessionToken != null) {
hardwareInputId = userState.sessionStateMap.get(
- sessionState.mHardwareSessionToken).mInfo.getId();
+ sessionState.hardwareSessionToken).info.getId();
break;
}
}
@@ -1601,8 +1400,8 @@
SessionState[] sessionStates = userState.sessionStateMap.values().toArray(
new SessionState[0]);
// Check if there is a wrapper input.
- if (sessionStates[0].mHardwareSessionToken != null
- || sessionStates[1].mHardwareSessionToken != null) {
+ if (sessionStates[0].hardwareSessionToken != null
+ || sessionStates[1].hardwareSessionToken != null) {
return true;
}
}
@@ -1662,15 +1461,15 @@
pw.increaseIndent();
- pw.println("mSessionTokens:");
+ pw.println("sessionTokens:");
pw.increaseIndent();
- for (IBinder token : client.mSessionTokens) {
+ for (IBinder token : client.sessionTokens) {
pw.println("" + token);
}
pw.decreaseIndent();
- pw.println("mClientTokens: " + client.mClientToken);
- pw.println("mUserId: " + client.mUserId);
+ pw.println("clientTokens: " + client.clientToken);
+ pw.println("userId: " + client.userId);
pw.decreaseIndent();
}
@@ -1685,17 +1484,17 @@
pw.increaseIndent();
- pw.println("mSessionTokens:");
+ pw.println("sessionTokens:");
pw.increaseIndent();
- for (IBinder token : service.mSessionTokens) {
+ for (IBinder token : service.sessionTokens) {
pw.println("" + token);
}
pw.decreaseIndent();
- pw.println("mService: " + service.mService);
- pw.println("mCallback: " + service.mCallback);
- pw.println("mBound: " + service.mBound);
- pw.println("mReconnecting: " + service.mReconnecting);
+ pw.println("service: " + service.service);
+ pw.println("callback: " + service.callback);
+ pw.println("bound: " + service.bound);
+ pw.println("reconnecting: " + service.reconnecting);
pw.decreaseIndent();
}
@@ -1709,15 +1508,15 @@
pw.println(entry.getKey() + ": " + session);
pw.increaseIndent();
- pw.println("mInfo: " + session.mInfo);
- pw.println("mClient: " + session.mClient);
- pw.println("mSeq: " + session.mSeq);
- pw.println("mCallingUid: " + session.mCallingUid);
- pw.println("mUserId: " + session.mUserId);
- pw.println("mSessionToken: " + session.mSessionToken);
- pw.println("mSession: " + session.mSession);
- pw.println("mLogUri: " + session.mLogUri);
- pw.println("mHardwareSessionToken: " + session.mHardwareSessionToken);
+ pw.println("info: " + session.info);
+ pw.println("client: " + session.client);
+ pw.println("seq: " + session.seq);
+ pw.println("callingUid: " + session.callingUid);
+ pw.println("userId: " + session.userId);
+ pw.println("sessionToken: " + session.sessionToken);
+ pw.println("session: " + session.session);
+ pw.println("logUri: " + session.logUri);
+ pw.println("hardwareSessionToken: " + session.hardwareSessionToken);
pw.decreaseIndent();
}
pw.decreaseIndent();
@@ -1736,19 +1535,6 @@
}
}
- private static final class TvInputState {
- // A TvInputInfo object which represents the TV input.
- private TvInputInfo mInfo;
-
- // The state of TV input. Connected by default.
- private int mState = INPUT_STATE_CONNECTED;
-
- @Override
- public String toString() {
- return "mInfo: " + mInfo + "; mState: " + mState;
- }
- }
-
private static final class UserState {
// A mapping from the TV input id to its TvInputState.
private Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>();
@@ -1789,104 +1575,117 @@
}
private final class ClientState implements IBinder.DeathRecipient {
- private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
+ private final List<IBinder> sessionTokens = new ArrayList<IBinder>();
- private IBinder mClientToken;
- private final int mUserId;
+ private IBinder clientToken;
+ private final int userId;
ClientState(IBinder clientToken, int userId) {
- mClientToken = clientToken;
- mUserId = userId;
+ this.clientToken = clientToken;
+ this.userId = userId;
}
public boolean isEmpty() {
- return mSessionTokens.isEmpty();
+ return sessionTokens.isEmpty();
}
@Override
public void binderDied() {
synchronized (mLock) {
- UserState userState = getUserStateLocked(mUserId);
+ UserState userState = getUserStateLocked(userId);
// DO NOT remove the client state of clientStateMap in this method. It will be
// removed in releaseSessionLocked().
- ClientState clientState = userState.clientStateMap.get(mClientToken);
+ ClientState clientState = userState.clientStateMap.get(clientToken);
if (clientState != null) {
- while (clientState.mSessionTokens.size() > 0) {
+ while (clientState.sessionTokens.size() > 0) {
releaseSessionLocked(
- clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId);
+ clientState.sessionTokens.get(0), Process.SYSTEM_UID, userId);
}
}
- mClientToken = null;
+ clientToken = null;
}
}
}
private final class ServiceState {
- private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
- private final ServiceConnection mConnection;
- private final ComponentName mComponent;
- private final boolean mIsHardware;
- private final List<TvInputInfo> mInputList = new ArrayList<TvInputInfo>();
+ private final List<IBinder> sessionTokens = new ArrayList<IBinder>();
+ private final ServiceConnection connection;
+ private final ComponentName component;
+ private final boolean isHardware;
+ private final List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
- private ITvInputService mService;
- private ServiceCallback mCallback;
- private boolean mBound;
- private boolean mReconnecting;
+ private ITvInputService service;
+ private ServiceCallback callback;
+ private boolean bound;
+ private boolean reconnecting;
private ServiceState(ComponentName component, int userId) {
- mComponent = component;
- mConnection = new InputServiceConnection(component, userId);
- mIsHardware = hasHardwarePermission(mContext.getPackageManager(), mComponent);
+ this.component = component;
+ this.connection = new InputServiceConnection(component, userId);
+ this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component);
+ }
+ }
+
+ private static final class TvInputState {
+ // A TvInputInfo object which represents the TV input.
+ private TvInputInfo info;
+
+ // The state of TV input. Connected by default.
+ private int state = INPUT_STATE_CONNECTED;
+
+ @Override
+ public String toString() {
+ return "info: " + info + "; state: " + state;
}
}
private final class SessionState implements IBinder.DeathRecipient {
- private final TvInputInfo mInfo;
- private final ITvInputClient mClient;
- private final int mSeq;
- private final int mCallingUid;
- private final int mUserId;
- private final IBinder mSessionToken;
- private ITvInputSession mSession;
- private Uri mLogUri;
+ private final TvInputInfo info;
+ private final ITvInputClient client;
+ private final int seq;
+ private final int callingUid;
+ private final int userId;
+ private final IBinder sessionToken;
+ private ITvInputSession session;
+ private Uri logUri;
// Not null if this session represents an external device connected to a hardware TV input.
- private IBinder mHardwareSessionToken;
+ private IBinder hardwareSessionToken;
private SessionState(IBinder sessionToken, TvInputInfo info, ITvInputClient client,
int seq, int callingUid, int userId) {
- mSessionToken = sessionToken;
- mInfo = info;
- mClient = client;
- mSeq = seq;
- mCallingUid = callingUid;
- mUserId = userId;
+ this.sessionToken = sessionToken;
+ this.info = info;
+ this.client = client;
+ this.seq = seq;
+ this.callingUid = callingUid;
+ this.userId = userId;
}
@Override
public void binderDied() {
synchronized (mLock) {
- mSession = null;
- if (mClient != null) {
+ session = null;
+ if (client != null) {
try {
- mClient.onSessionReleased(mSeq);
+ client.onSessionReleased(seq);
} catch(RemoteException e) {
Slog.e(TAG, "error in onSessionReleased", e);
}
}
// If there are any other sessions based on this session, they should be released.
- UserState userState = getUserStateLocked(mUserId);
+ UserState userState = getUserStateLocked(userId);
for (SessionState sessionState : userState.sessionStateMap.values()) {
- if (mSessionToken == sessionState.mHardwareSessionToken) {
- releaseSessionLocked(sessionState.mSessionToken, Process.SYSTEM_UID,
- mUserId);
+ if (sessionToken == sessionState.hardwareSessionToken) {
+ releaseSessionLocked(sessionState.sessionToken, Process.SYSTEM_UID,
+ userId);
try {
- sessionState.mClient.onSessionReleased(sessionState.mSeq);
+ sessionState.client.onSessionReleased(sessionState.seq);
} catch (RemoteException e) {
Slog.e(TAG, "error in onSessionReleased", e);
}
}
}
- removeSessionStateLocked(mSessionToken, mUserId);
+ removeSessionStateLocked(sessionToken, userId);
}
}
}
@@ -1908,37 +1707,37 @@
synchronized (mLock) {
UserState userState = getUserStateLocked(mUserId);
ServiceState serviceState = userState.serviceStateMap.get(mComponent);
- serviceState.mService = ITvInputService.Stub.asInterface(service);
+ serviceState.service = ITvInputService.Stub.asInterface(service);
// Register a callback, if we need to.
- if (serviceState.mIsHardware && serviceState.mCallback == null) {
- serviceState.mCallback = new ServiceCallback(mComponent, mUserId);
+ if (serviceState.isHardware && serviceState.callback == null) {
+ serviceState.callback = new ServiceCallback(mComponent, mUserId);
try {
- serviceState.mService.registerCallback(serviceState.mCallback);
+ serviceState.service.registerCallback(serviceState.callback);
} catch (RemoteException e) {
Slog.e(TAG, "error in registerCallback", e);
}
}
// And create sessions, if any.
- for (IBinder sessionToken : serviceState.mSessionTokens) {
- createSessionInternalLocked(serviceState.mService, sessionToken, mUserId);
+ for (IBinder sessionToken : serviceState.sessionTokens) {
+ createSessionInternalLocked(serviceState.service, sessionToken, mUserId);
}
for (TvInputState inputState : userState.inputMap.values()) {
- if (inputState.mInfo.getComponent().equals(component)
- && inputState.mState != INPUT_STATE_DISCONNECTED) {
- notifyInputStateChangedLocked(userState, inputState.mInfo.getId(),
- inputState.mState, null);
+ if (inputState.info.getComponent().equals(component)
+ && inputState.state != INPUT_STATE_DISCONNECTED) {
+ notifyInputStateChangedLocked(userState, inputState.info.getId(),
+ inputState.state, null);
}
}
- if (serviceState.mIsHardware) {
+ if (serviceState.isHardware) {
List<TvInputHardwareInfo> hardwareInfoList =
mTvInputHardwareManager.getHardwareList();
for (TvInputHardwareInfo hardwareInfo : hardwareInfoList) {
try {
- serviceState.mService.notifyHardwareAdded(hardwareInfo);
+ serviceState.service.notifyHardwareAdded(hardwareInfo);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHardwareAdded", e);
}
@@ -1948,7 +1747,7 @@
mTvInputHardwareManager.getHdmiDeviceList();
for (HdmiDeviceInfo deviceInfo : deviceInfoList) {
try {
- serviceState.mService.notifyHdmiDeviceAdded(deviceInfo);
+ serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
}
@@ -1970,16 +1769,16 @@
UserState userState = getUserStateLocked(mUserId);
ServiceState serviceState = userState.serviceStateMap.get(mComponent);
if (serviceState != null) {
- serviceState.mReconnecting = true;
- serviceState.mBound = false;
- serviceState.mService = null;
- serviceState.mCallback = null;
+ serviceState.reconnecting = true;
+ serviceState.bound = false;
+ serviceState.service = null;
+ serviceState.callback = null;
abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId);
for (TvInputState inputState : userState.inputMap.values()) {
- if (inputState.mInfo.getComponent().equals(component)) {
- notifyInputStateChangedLocked(userState, inputState.mInfo.getId(),
+ if (inputState.info.getComponent().equals(component)) {
+ notifyInputStateChangedLocked(userState, inputState.info.getId(),
INPUT_STATE_DISCONNECTED, null);
}
}
@@ -2012,7 +1811,7 @@
private void addTvInputLocked(TvInputInfo inputInfo) {
ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
- serviceState.mInputList.add(inputInfo);
+ serviceState.inputList.add(inputInfo);
buildTvInputListLocked(mUserId);
}
@@ -2042,7 +1841,7 @@
synchronized (mLock) {
ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
boolean removed = false;
- for (Iterator<TvInputInfo> it = serviceState.mInputList.iterator();
+ for (Iterator<TvInputInfo> it = serviceState.inputList.iterator();
it.hasNext(); ) {
if (it.next().getId().equals(inputId)) {
it.remove();
@@ -2060,7 +1859,211 @@
}
}
- private final class WatchLogHandler extends Handler {
+ private final class SessionCallback extends ITvInputSessionCallback.Stub {
+ private final SessionState sessionState;
+ private final InputChannel[] mChannels;
+
+ SessionCallback(SessionState sessionState, InputChannel[] channels) {
+ this.sessionState = sessionState;
+ mChannels = channels;
+ }
+
+ @Override
+ public void onSessionCreated(ITvInputSession session, IBinder harewareSessionToken) {
+ if (DEBUG) {
+ Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.info.getId() + ")");
+ }
+ synchronized (mLock) {
+ sessionState.session = session;
+ sessionState.hardwareSessionToken = harewareSessionToken;
+ if (session == null) {
+ removeSessionStateLocked(sessionState.sessionToken, sessionState.userId);
+ sendSessionTokenToClientLocked(sessionState.client,
+ sessionState.info.getId(), null, null, sessionState.seq);
+ } else {
+ try {
+ session.asBinder().linkToDeath(sessionState, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "session process has already died", e);
+ }
+
+ IBinder clientToken = sessionState.client.asBinder();
+ UserState userState = getUserStateLocked(sessionState.userId);
+ ClientState clientState = userState.clientStateMap.get(clientToken);
+ if (clientState == null) {
+ clientState = createClientStateLocked(clientToken, sessionState.userId);
+ }
+ clientState.sessionTokens.add(sessionState.sessionToken);
+
+ sendSessionTokenToClientLocked(sessionState.client,
+ sessionState.info.getId(), sessionState.sessionToken, mChannels[0],
+ sessionState.seq);
+ }
+ mChannels[0].dispose();
+ }
+ }
+
+ @Override
+ public void onChannelRetuned(Uri channelUri) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ // TODO: Consider adding this channel change in the watch log. When we do
+ // that, how we can protect the watch log from malicious tv inputs should
+ // be addressed. e.g. add a field which represents where the channel change
+ // originated from.
+ sessionState.client.onChannelRetuned(channelUri, sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onChannelRetuned", e);
+ }
+ }
+ }
+
+ @Override
+ public void onTracksChanged(List<TvTrackInfo> tracks) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onTracksChanged(" + tracks + ")");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ sessionState.client.onTracksChanged(tracks, sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onTracksChanged", e);
+ }
+ }
+ }
+
+ @Override
+ public void onTrackSelected(int type, String trackId) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ sessionState.client.onTrackSelected(type, trackId, sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onTrackSelected", e);
+ }
+ }
+ }
+
+ @Override
+ public void onVideoAvailable() {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onVideoAvailable()");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ sessionState.client.onVideoAvailable(sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onVideoAvailable", e);
+ }
+ }
+ }
+
+ @Override
+ public void onVideoUnavailable(int reason) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ sessionState.client.onVideoUnavailable(reason, sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onVideoUnavailable", e);
+ }
+ }
+ }
+
+ @Override
+ public void onContentAllowed() {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onContentAllowed()");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ sessionState.client.onContentAllowed(sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onContentAllowed", e);
+ }
+ }
+ }
+
+ @Override
+ public void onContentBlocked(String rating) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onContentBlocked()");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ sessionState.client.onContentBlocked(rating, sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onContentBlocked", e);
+ }
+ }
+ }
+
+ @Override
+ public void onLayoutSurface(int left, int top, int right, int bottom) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
+ + ", right=" + right + ", bottom=" + bottom + ",)");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ sessionState.client.onLayoutSurface(left, top, right, bottom, sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onLayoutSurface", e);
+ }
+ }
+ }
+
+ @Override
+ public void onSessionEvent(String eventType, Bundle eventArgs) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ sessionState.client.onSessionEvent(eventType, eventArgs,
+ sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onSessionEvent", e);
+ }
+ }
+ }
+ }
+
+ private static final class WatchLogHandler extends Handler {
// There are only two kinds of watch events that can happen on the system:
// 1. The current TV input session is tuned to a new channel.
// 2. The session is released for some reason.
@@ -2072,8 +2075,11 @@
private static final int MSG_LOG_WATCH_START = 1;
private static final int MSG_LOG_WATCH_END = 2;
- public WatchLogHandler(Looper looper) {
+ private final ContentResolver mContentResolver;
+
+ public WatchLogHandler(ContentResolver contentResolver, Looper looper) {
super(looper);
+ mContentResolver = contentResolver;
}
@Override
@@ -2159,7 +2165,7 @@
}
}
- final class HardwareListener implements TvInputHardwareManager.Listener {
+ private final class HardwareListener implements TvInputHardwareManager.Listener {
@Override
public void onStateChanged(String inputId, int state) {
synchronized (mLock) {
@@ -2173,9 +2179,9 @@
UserState userState = getUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
- if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+ if (!serviceState.isHardware || serviceState.service == null) continue;
try {
- serviceState.mService.notifyHardwareAdded(info);
+ serviceState.service.notifyHardwareAdded(info);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHardwareAdded", e);
}
@@ -2189,9 +2195,9 @@
UserState userState = getUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
- if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+ if (!serviceState.isHardware || serviceState.service == null) continue;
try {
- serviceState.mService.notifyHardwareRemoved(info);
+ serviceState.service.notifyHardwareRemoved(info);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHardwareRemoved", e);
}
@@ -2205,9 +2211,9 @@
UserState userState = getUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
- if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+ if (!serviceState.isHardware || serviceState.service == null) continue;
try {
- serviceState.mService.notifyHdmiDeviceAdded(deviceInfo);
+ serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
}
@@ -2221,9 +2227,9 @@
UserState userState = getUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
- if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+ if (!serviceState.isHardware || serviceState.service == null) continue;
try {
- serviceState.mService.notifyHdmiDeviceRemoved(deviceInfo);
+ serviceState.service.notifyHdmiDeviceRemoved(deviceInfo);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index ef74205..69c9144 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -114,6 +114,10 @@
transformation.clear();
transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
hasTransformation = true;
+
+ if (!mAppToken.appFullscreen) {
+ anim.setBackgroundColor(0);
+ }
}
public void setDummyAnimation() {
diff --git a/telecomm/java/android/telecomm/PhoneAccount.java b/telecomm/java/android/telecomm/PhoneAccount.java
index 411f48c..97a7b9d 100644
--- a/telecomm/java/android/telecomm/PhoneAccount.java
+++ b/telecomm/java/android/telecomm/PhoneAccount.java
@@ -93,7 +93,7 @@
private CharSequence mLabel;
private CharSequence mShortDescription;
- private Builder() {}
+ public Builder() {}
public Builder withAccountHandle(PhoneAccountHandle value) {
this.mAccountHandle = value;
diff --git a/telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl b/telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl
index 42c77d7..0ab7564 100644
--- a/telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl
+++ b/telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl
@@ -20,6 +20,8 @@
/**
* Simple response callback object.
+ *
+ * {@hide}
*/
oneway interface RemoteServiceCallback {
void onError();
diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
index b6591bd..c08c1a3 100644
--- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
+++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
@@ -23,7 +23,6 @@
import android.content.Intent;
import android.os.Bundle;
import android.text.format.DateUtils;
-import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -36,6 +35,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Map;
public class UsageStatsActivity extends ListActivity {
private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
@@ -84,7 +84,7 @@
private void updateAdapter() {
long now = System.currentTimeMillis();
long beginTime = now - USAGE_STATS_PERIOD;
- ArrayMap<String, UsageStats> stats = mUsageStatsManager.queryAndAggregateUsageStats(
+ Map<String, UsageStats> stats = mUsageStatsManager.queryAndAggregateUsageStats(
beginTime, now);
mAdapter.update(stats);
}
@@ -92,17 +92,13 @@
private class Adapter extends BaseAdapter {
private ArrayList<UsageStats> mStats = new ArrayList<>();
- public void update(ArrayMap<String, UsageStats> stats) {
+ public void update(Map<String, UsageStats> stats) {
mStats.clear();
if (stats == null) {
return;
}
- final int packageCount = stats.size();
- for (int i = 0; i < packageCount; i++) {
- mStats.add(stats.valueAt(i));
- }
-
+ mStats.addAll(stats.values());
Collections.sort(mStats, mComparator);
notifyDataSetChanged();
}
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
index e1a579c..1f01461 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
@@ -92,8 +92,7 @@
break;
case AlwaysOnHotwordDetector.STATE_KEYPHRASE_UNENROLLED:
Log.i(TAG, "STATE_KEYPHRASE_UNENROLLED");
- Intent enroll = mHotwordDetector.getManageIntent(
- AlwaysOnHotwordDetector.MANAGE_ACTION_ENROLL);
+ Intent enroll = mHotwordDetector.createIntentToEnroll();
Log.i(TAG, "Need to enroll with " + enroll);
break;
case AlwaysOnHotwordDetector.STATE_KEYPHRASE_ENROLLED:
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index b44e2d1..117fc24 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -1594,6 +1594,11 @@
return mIncludedAssets.getResources(false);
}
+AssetManager& AaptAssets::getAssetManager()
+{
+ return mIncludedAssets;
+}
+
void AaptAssets::print(const String8& prefix) const
{
String8 innerPrefix(prefix);
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 0c2576a..3fc9f81 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -561,6 +561,7 @@
status_t buildIncludedResources(Bundle* bundle);
status_t addIncludedResources(const sp<AaptFile>& file);
const ResTable& getIncludedResources() const;
+ AssetManager& getAssetManager();
void print(const String8& prefix) const;
diff --git a/tools/aapt/AaptXml.cpp b/tools/aapt/AaptXml.cpp
new file mode 100644
index 0000000..708e405
--- /dev/null
+++ b/tools/aapt/AaptXml.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <androidfw/ResourceTypes.h>
+#include <utils/String8.h>
+
+#include "AaptXml.h"
+
+using namespace android;
+
+namespace AaptXml {
+
+static String8 getStringAttributeAtIndex(const ResXMLTree& tree, ssize_t attrIndex,
+ String8* outError) {
+ Res_value value;
+ if (tree.getAttributeValue(attrIndex, &value) < 0) {
+ if (outError != NULL) {
+ *outError = "could not find attribute at index";
+ }
+ return String8();
+ }
+
+ if (value.dataType != Res_value::TYPE_STRING) {
+ if (outError != NULL) {
+ *outError = "attribute is not a string value";
+ }
+ return String8();
+ }
+
+ size_t len;
+ const uint16_t* str = tree.getAttributeStringValue(attrIndex, &len);
+ return str ? String8(str, len) : String8();
+}
+
+static int32_t getIntegerAttributeAtIndex(const ResXMLTree& tree, ssize_t attrIndex,
+ int32_t defValue, String8* outError) {
+ Res_value value;
+ if (tree.getAttributeValue(attrIndex, &value) < 0) {
+ if (outError != NULL) {
+ *outError = "could not find attribute at index";
+ }
+ return defValue;
+ }
+
+ if (value.dataType < Res_value::TYPE_FIRST_INT
+ || value.dataType > Res_value::TYPE_LAST_INT) {
+ if (outError != NULL) {
+ *outError = "attribute is not an integer value";
+ }
+ return defValue;
+ }
+ return value.data;
+}
+
+
+ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes) {
+ size_t attrCount = tree.getAttributeCount();
+ for (size_t i = 0; i < attrCount; i++) {
+ if (tree.getAttributeNameResID(i) == attrRes) {
+ return (ssize_t)i;
+ }
+ }
+ return -1;
+}
+
+String8 getAttribute(const ResXMLTree& tree, const char* ns,
+ const char* attr, String8* outError) {
+ ssize_t idx = tree.indexOfAttribute(ns, attr);
+ if (idx < 0) {
+ return String8();
+ }
+ return getStringAttributeAtIndex(tree, idx, outError);
+}
+
+String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError) {
+ ssize_t idx = indexOfAttribute(tree, attrRes);
+ if (idx < 0) {
+ return String8();
+ }
+ return getStringAttributeAtIndex(tree, idx, outError);
+}
+
+String8 getResolvedAttribute(const ResTable& resTable, const ResXMLTree& tree,
+ uint32_t attrRes, String8* outError) {
+ ssize_t idx = indexOfAttribute(tree, attrRes);
+ if (idx < 0) {
+ return String8();
+ }
+ Res_value value;
+ if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
+ if (value.dataType == Res_value::TYPE_STRING) {
+ size_t len;
+ const uint16_t* str = tree.getAttributeStringValue(idx, &len);
+ return str ? String8(str, len) : String8();
+ }
+ resTable.resolveReference(&value, 0);
+ if (value.dataType != Res_value::TYPE_STRING) {
+ if (outError != NULL) {
+ *outError = "attribute is not a string value";
+ }
+ return String8();
+ }
+ }
+ size_t len;
+ const Res_value* value2 = &value;
+ const char16_t* str = resTable.valueToString(value2, 0, NULL, &len);
+ return str ? String8(str, len) : String8();
+}
+
+int32_t getIntegerAttribute(const ResXMLTree& tree, const char* ns,
+ const char* attr, int32_t defValue, String8* outError) {
+ ssize_t idx = tree.indexOfAttribute(ns, attr);
+ if (idx < 0) {
+ return defValue;
+ }
+ return getIntegerAttributeAtIndex(tree, idx, defValue, outError);
+}
+
+int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, int32_t defValue,
+ String8* outError) {
+ ssize_t idx = indexOfAttribute(tree, attrRes);
+ if (idx < 0) {
+ return defValue;
+ }
+ return getIntegerAttributeAtIndex(tree, idx, defValue, outError);
+}
+
+int32_t getResolvedIntegerAttribute(const ResTable& resTable, const ResXMLTree& tree,
+ uint32_t attrRes, int32_t defValue, String8* outError) {
+ ssize_t idx = indexOfAttribute(tree, attrRes);
+ if (idx < 0) {
+ return defValue;
+ }
+ Res_value value;
+ if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
+ if (value.dataType == Res_value::TYPE_REFERENCE) {
+ resTable.resolveReference(&value, 0);
+ }
+ if (value.dataType < Res_value::TYPE_FIRST_INT
+ || value.dataType > Res_value::TYPE_LAST_INT) {
+ if (outError != NULL) {
+ *outError = "attribute is not an integer value";
+ }
+ return defValue;
+ }
+ }
+ return value.data;
+}
+
+void getResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
+ uint32_t attrRes, Res_value* outValue, String8* outError) {
+ ssize_t idx = indexOfAttribute(tree, attrRes);
+ if (idx < 0) {
+ if (outError != NULL) {
+ *outError = "attribute could not be found";
+ }
+ return;
+ }
+ if (tree.getAttributeValue(idx, outValue) != NO_ERROR) {
+ if (outValue->dataType == Res_value::TYPE_REFERENCE) {
+ resTable.resolveReference(outValue, 0);
+ }
+ // The attribute was found and was resolved if need be.
+ return;
+ }
+ if (outError != NULL) {
+ *outError = "error getting resolved resource attribute";
+ }
+}
+
+} // namespace AaptXml
diff --git a/tools/aapt/AaptXml.h b/tools/aapt/AaptXml.h
new file mode 100644
index 0000000..16977f3
--- /dev/null
+++ b/tools/aapt/AaptXml.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AAPT_XML_H
+#define __AAPT_XML_H
+
+#include <androidfw/ResourceTypes.h>
+#include <utils/String8.h>
+
+/**
+ * Utility methods for dealing with ResXMLTree.
+ */
+namespace AaptXml {
+
+/**
+ * Returns the index of the attribute, or < 0 if it was not found.
+ */
+ssize_t indexOfAttribute(const android::ResXMLTree& tree, uint32_t attrRes);
+
+/**
+ * Returns the string value for the specified attribute.
+ * The string must be present in the ResXMLTree's string pool (inline in the XML).
+ */
+android::String8 getAttribute(const android::ResXMLTree& tree, const char* ns,
+ const char* attr, android::String8* outError = NULL);
+
+/**
+ * Returns the string value for the specified attribute, or an empty string
+ * if the attribute does not exist.
+ * The string must be present in the ResXMLTree's string pool (inline in the XML).
+ */
+android::String8 getAttribute(const android::ResXMLTree& tree, uint32_t attrRes,
+ android::String8* outError = NULL);
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer must be declared inline in the XML.
+ */
+int32_t getIntegerAttribute(const android::ResXMLTree& tree, const char* ns,
+ const char* attr, int32_t defValue = -1, android::String8* outError = NULL);
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer must be declared inline in the XML.
+ */
+inline int32_t getIntegerAttribute(const android::ResXMLTree& tree, const char* ns,
+ const char* attr, android::String8* outError) {
+ return getIntegerAttribute(tree, ns, attr, -1, outError);
+}
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer must be declared inline in the XML.
+ */
+int32_t getIntegerAttribute(const android::ResXMLTree& tree, uint32_t attrRes,
+ int32_t defValue = -1, android::String8* outError = NULL);
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer must be declared inline in the XML.
+ */
+inline int32_t getIntegerAttribute(const android::ResXMLTree& tree, uint32_t attrRes,
+ android::String8* outError) {
+ return getIntegerAttribute(tree, attrRes, -1, outError);
+}
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer may be a resource in the supplied ResTable.
+ */
+int32_t getResolvedIntegerAttribute(const android::ResTable& resTable,
+ const android::ResXMLTree& tree, uint32_t attrRes, int32_t defValue = -1,
+ android::String8* outError = NULL);
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer may be a resource in the supplied ResTable.
+ */
+inline int32_t getResolvedIntegerAttribute(const android::ResTable& resTable,
+ const android::ResXMLTree& tree, uint32_t attrRes,
+ android::String8* outError) {
+ return getResolvedIntegerAttribute(resTable, tree, attrRes, -1, outError);
+}
+
+/**
+ * Returns the string value for the specified attribute, or an empty string
+ * if the attribute does not exist.
+ * The string may be a resource in the supplied ResTable.
+ */
+android::String8 getResolvedAttribute(const android::ResTable& resTable,
+ const android::ResXMLTree& tree, uint32_t attrRes,
+ android::String8* outError = NULL);
+
+/**
+ * Returns the resource for the specified attribute in the outValue parameter.
+ * The resource may be a resource in the supplied ResTable.
+ */
+void getResolvedResourceAttribute(const android::ResTable& resTable,
+ const android::ResXMLTree& tree, uint32_t attrRes, android::Res_value* outValue,
+ android::String8* outError = NULL);
+
+} // namespace AaptXml
+
+#endif // __AAPT_XML_H
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 4ce5045..2cbabe1 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -28,6 +28,7 @@
AaptAssets.cpp \
AaptConfig.cpp \
AaptUtil.cpp \
+ AaptXml.cpp \
ApkBuilder.cpp \
Command.cpp \
CrunchCache.cpp \
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index af49461..9bed899 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -130,6 +130,10 @@
void setErrorOnFailedInsert(bool val) { mErrorOnFailedInsert = val; }
bool getErrorOnMissingConfigEntry() { return mErrorOnMissingConfigEntry; }
void setErrorOnMissingConfigEntry(bool val) { mErrorOnMissingConfigEntry = val; }
+ const android::String8& getPlatformBuildVersionCode() { return mPlatformVersionCode; }
+ void setPlatformBuildVersionCode(const android::String8& code) { mPlatformVersionCode = code; }
+ const android::String8& getPlatformBuildVersionName() { return mPlatformVersionName; }
+ void setPlatformBuildVersionName(const android::String8& name) { mPlatformVersionName = name; }
bool getUTF16StringsOption() {
return mWantUTF16 || !isMinSdkAtLeast(SDK_FROYO);
@@ -323,6 +327,8 @@
const char* mSingleCrunchInputFile;
const char* mSingleCrunchOutputFile;
bool mBuildSharedLibrary;
+ android::String8 mPlatformVersionCode;
+ android::String8 mPlatformVersionName;
/* file specification */
int mArgc;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index a0f0a08..27e60f3 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -3,6 +3,7 @@
//
// Android Asset Packaging Tool main entry point.
//
+#include "AaptXml.h"
#include "ApkBuilder.h"
#include "Bundle.h"
#include "Images.h"
@@ -241,162 +242,17 @@
return result;
}
-static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
-{
- size_t N = tree.getAttributeCount();
- for (size_t i=0; i<N; i++) {
- if (tree.getAttributeNameResID(i) == attrRes) {
- return (ssize_t)i;
- }
- }
- return -1;
-}
-
-String8 getAttribute(const ResXMLTree& tree, const char* ns,
- const char* attr, String8* outError)
-{
- ssize_t idx = tree.indexOfAttribute(ns, attr);
- if (idx < 0) {
- return String8();
- }
- Res_value value;
- if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
- if (value.dataType != Res_value::TYPE_STRING) {
- if (outError != NULL) {
- *outError = "attribute is not a string value";
- }
- return String8();
- }
- }
- size_t len;
- const uint16_t* str = tree.getAttributeStringValue(idx, &len);
- return str ? String8(str, len) : String8();
-}
-
-static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
-{
- ssize_t idx = indexOfAttribute(tree, attrRes);
- if (idx < 0) {
- return String8();
- }
- Res_value value;
- if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
- if (value.dataType != Res_value::TYPE_STRING) {
- if (outError != NULL) {
- *outError = "attribute is not a string value";
- }
- return String8();
- }
- }
- size_t len;
- const uint16_t* str = tree.getAttributeStringValue(idx, &len);
- return str ? String8(str, len) : String8();
-}
-
-static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
- String8* outError, int32_t defValue = -1)
-{
- ssize_t idx = indexOfAttribute(tree, attrRes);
- if (idx < 0) {
- return defValue;
- }
- Res_value value;
- if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
- if (value.dataType < Res_value::TYPE_FIRST_INT
- || value.dataType > Res_value::TYPE_LAST_INT) {
- if (outError != NULL) {
- *outError = "attribute is not an integer value";
- }
- return defValue;
- }
- }
- return value.data;
-}
-
-static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
- uint32_t attrRes, String8* outError, int32_t defValue = -1)
-{
- ssize_t idx = indexOfAttribute(tree, attrRes);
- if (idx < 0) {
- return defValue;
- }
- Res_value value;
- if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
- if (value.dataType == Res_value::TYPE_REFERENCE) {
- resTable->resolveReference(&value, 0);
- }
- if (value.dataType < Res_value::TYPE_FIRST_INT
- || value.dataType > Res_value::TYPE_LAST_INT) {
- if (outError != NULL) {
- *outError = "attribute is not an integer value";
- }
- return defValue;
- }
- }
- return value.data;
-}
-
-static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
- uint32_t attrRes, String8* outError)
-{
- ssize_t idx = indexOfAttribute(tree, attrRes);
- if (idx < 0) {
- return String8();
- }
- Res_value value;
- if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
- if (value.dataType == Res_value::TYPE_STRING) {
- size_t len;
- const uint16_t* str = tree.getAttributeStringValue(idx, &len);
- return str ? String8(str, len) : String8();
- }
- resTable->resolveReference(&value, 0);
- if (value.dataType != Res_value::TYPE_STRING) {
- if (outError != NULL) {
- *outError = "attribute is not a string value";
- }
- return String8();
- }
- }
- size_t len;
- const Res_value* value2 = &value;
- const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
- return str ? String8(str, len) : String8();
-}
-
-static void getResolvedResourceAttribute(Res_value* value, const ResTable* resTable,
- const ResXMLTree& tree, uint32_t attrRes, String8* outError)
-{
- ssize_t idx = indexOfAttribute(tree, attrRes);
- if (idx < 0) {
- if (outError != NULL) {
- *outError = "attribute could not be found";
- }
- return;
- }
- if (tree.getAttributeValue(idx, value) != NO_ERROR) {
- if (value->dataType == Res_value::TYPE_REFERENCE) {
- resTable->resolveReference(value, 0);
- }
- // The attribute was found and was resolved if need be.
- return;
- }
- if (outError != NULL) {
- *outError = "error getting resolved resource attribute";
- }
-}
-
-static void printResolvedResourceAttribute(const ResTable* resTable, const ResXMLTree& tree,
+static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
uint32_t attrRes, String8 attrLabel, String8* outError)
{
Res_value value;
- getResolvedResourceAttribute(&value, resTable, tree, attrRes, outError);
+ AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
if (*outError != "") {
*outError = "error print resolved resource attribute";
return;
}
if (value.dataType == Res_value::TYPE_STRING) {
- String8 result = getResolvedAttribute(resTable, tree, attrRes, outError);
+ String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
printf("%s='%s'", attrLabel.string(),
ResTable::normalizeForOutput(result.string()).string());
} else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
@@ -488,10 +344,10 @@
}
String8 tag(ctag16);
if (tag == "screen") {
- int32_t screenSize = getIntegerAttribute(tree,
- SCREEN_SIZE_ATTR, NULL, -1);
- int32_t screenDensity = getIntegerAttribute(tree,
- SCREEN_DENSITY_ATTR, NULL, -1);
+ int32_t screenSize = AaptXml::getIntegerAttribute(tree,
+ SCREEN_SIZE_ATTR);
+ int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
+ SCREEN_DENSITY_ATTR);
if (screenSize > 0 && screenDensity > 0) {
if (!first) {
printf(",");
@@ -577,7 +433,7 @@
}
} else if (depth == 2 && withinApduService) {
if (tag == "aid-group") {
- String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
+ String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
if (error != "") {
if (outError != NULL) *outError = error;
return Vector<String8>();
@@ -876,11 +732,11 @@
fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
goto bail;
}
- String8 pkg = getAttribute(tree, NULL, "package", NULL);
+ String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
} else if (depth == 2 && tag == "permission") {
String8 error;
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR: %s\n", error.string());
goto bail;
@@ -889,14 +745,14 @@
ResTable::normalizeForOutput(name.string()).string());
} else if (depth == 2 && tag == "uses-permission") {
String8 error;
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR: %s\n", error.string());
goto bail;
}
printUsesPermission(name,
- getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
- getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
+ AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
+ AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
}
}
} else if (strcmp("badging", option) == 0) {
@@ -1151,12 +1007,14 @@
fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
goto bail;
}
- pkg = getAttribute(tree, NULL, "package", NULL);
+ pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
printf("package: name='%s' ",
ResTable::normalizeForOutput(pkg.string()).string());
- int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
+ int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
+ &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
+ error.string());
goto bail;
}
if (versionCode > 0) {
@@ -1164,23 +1022,29 @@
} else {
printf("versionCode='' ");
}
- String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
+ String8 versionName = AaptXml::getResolvedAttribute(res, tree,
+ VERSION_NAME_ATTR, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
+ error.string());
goto bail;
}
printf("versionName='%s'",
ResTable::normalizeForOutput(versionName.string()).string());
- String8 splitName = getAttribute(tree, NULL, "split", NULL);
+ String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
if (!splitName.isEmpty()) {
printf(" split='%s'", ResTable::normalizeForOutput(
splitName.string()).string());
}
+
+ String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
+ "platformBuildVersionName");
+ printf(" platformBuildVersionName='%s'", platformVersionName.string());
printf("\n");
- int32_t installLocation = getResolvedIntegerAttribute(&res, tree,
- INSTALL_LOCATION_ATTR, &error, -1);
+ int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
+ INSTALL_LOCATION_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
error.string());
@@ -1215,7 +1079,8 @@
for (size_t i=0; i<NL; i++) {
const char* localeStr = locales[i].string();
assets.setLocale(localeStr != NULL ? localeStr : "");
- String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+ String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
+ &error);
if (llabel != "") {
if (localeStr == NULL || strlen(localeStr) == 0) {
label = llabel;
@@ -1236,7 +1101,8 @@
for (size_t i=0; i<ND; i++) {
tmpConfig.density = densities[i];
assets.setConfiguration(tmpConfig);
- String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+ String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
+ &error);
if (icon != "") {
printf("application-icon-%d:'%s'\n", densities[i],
ResTable::normalizeForOutput(icon.string()).string());
@@ -1244,14 +1110,17 @@
}
assets.setConfiguration(config);
- String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+ String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
+ error.string());
goto bail;
}
- int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
+ int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
+ &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
+ error.string());
goto bail;
}
printf("application: label='%s' ",
@@ -1261,9 +1130,11 @@
printf("testOnly='%d'\n", testOnly);
}
- int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
+ int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
+ DEBUGGABLE_ATTR, 0, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
+ error.string());
goto bail;
}
if (debuggable != 0) {
@@ -1284,10 +1155,11 @@
}
}
} else if (tag == "uses-sdk") {
- int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
+ int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
if (error != "") {
error = "";
- String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
+ String8 name = AaptXml::getResolvedAttribute(res, tree,
+ MIN_SDK_VERSION_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
error.string());
@@ -1300,14 +1172,15 @@
targetSdk = code;
printf("sdkVersion:'%d'\n", code);
}
- code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
+ code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
if (code != -1) {
printf("maxSdkVersion:'%d'\n", code);
}
- code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
+ code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
if (error != "") {
error = "";
- String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
+ String8 name = AaptXml::getResolvedAttribute(res, tree,
+ TARGET_SDK_VERSION_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
error.string());
@@ -1323,16 +1196,16 @@
printf("targetSdkVersion:'%d'\n", code);
}
} else if (tag == "uses-configuration") {
- int32_t reqTouchScreen = getIntegerAttribute(tree,
- REQ_TOUCH_SCREEN_ATTR, NULL, 0);
- int32_t reqKeyboardType = getIntegerAttribute(tree,
- REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
- int32_t reqHardKeyboard = getIntegerAttribute(tree,
- REQ_HARD_KEYBOARD_ATTR, NULL, 0);
- int32_t reqNavigation = getIntegerAttribute(tree,
- REQ_NAVIGATION_ATTR, NULL, 0);
- int32_t reqFiveWayNav = getIntegerAttribute(tree,
- REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
+ int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
+ REQ_TOUCH_SCREEN_ATTR, 0);
+ int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
+ REQ_KEYBOARD_TYPE_ATTR, 0);
+ int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
+ REQ_HARD_KEYBOARD_ATTR, 0);
+ int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
+ REQ_NAVIGATION_ATTR, 0);
+ int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
+ REQ_FIVE_WAY_NAV_ATTR, 0);
printf("uses-configuration:");
if (reqTouchScreen != 0) {
printf(" reqTouchScreen='%d'", reqTouchScreen);
@@ -1353,26 +1226,26 @@
} else if (tag == "supports-input") {
withinSupportsInput = true;
} else if (tag == "supports-screens") {
- smallScreen = getIntegerAttribute(tree,
- SMALL_SCREEN_ATTR, NULL, 1);
- normalScreen = getIntegerAttribute(tree,
- NORMAL_SCREEN_ATTR, NULL, 1);
- largeScreen = getIntegerAttribute(tree,
- LARGE_SCREEN_ATTR, NULL, 1);
- xlargeScreen = getIntegerAttribute(tree,
- XLARGE_SCREEN_ATTR, NULL, 1);
- anyDensity = getIntegerAttribute(tree,
- ANY_DENSITY_ATTR, NULL, 1);
- requiresSmallestWidthDp = getIntegerAttribute(tree,
- REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
- compatibleWidthLimitDp = getIntegerAttribute(tree,
- COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
- largestWidthLimitDp = getIntegerAttribute(tree,
- LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
+ smallScreen = AaptXml::getIntegerAttribute(tree,
+ SMALL_SCREEN_ATTR, 1);
+ normalScreen = AaptXml::getIntegerAttribute(tree,
+ NORMAL_SCREEN_ATTR, 1);
+ largeScreen = AaptXml::getIntegerAttribute(tree,
+ LARGE_SCREEN_ATTR, 1);
+ xlargeScreen = AaptXml::getIntegerAttribute(tree,
+ XLARGE_SCREEN_ATTR, 1);
+ anyDensity = AaptXml::getIntegerAttribute(tree,
+ ANY_DENSITY_ATTR, 1);
+ requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
+ REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
+ compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
+ COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
+ largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
+ LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
} else if (tag == "feature-group") {
withinFeatureGroup = true;
FeatureGroup group;
- group.label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+ group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:label' attribute:"
" %s\n", error.string());
@@ -1381,17 +1254,17 @@
featureGroups.add(group);
} else if (tag == "uses-feature") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
- int req = getIntegerAttribute(tree,
- REQUIRED_ATTR, NULL, 1);
+ int req = AaptXml::getIntegerAttribute(tree,
+ REQUIRED_ATTR, 1);
commonFeatures.features.add(name, req);
if (req) {
addParentFeatures(&commonFeatures, name);
}
} else {
- int vers = getIntegerAttribute(tree,
+ int vers = AaptXml::getIntegerAttribute(tree,
GL_ES_VERSION_ATTR, &error);
if (error == "") {
if (vers > commonFeatures.openGLESVersion) {
@@ -1400,7 +1273,7 @@
}
}
} else if (tag == "uses-permission") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
if (name == "android.permission.CAMERA") {
addImpliedFeature(&impliedFeatures, "android.hardware.camera",
@@ -1478,15 +1351,15 @@
}
printUsesPermission(name,
- getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
- getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
+ AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
+ AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
} else {
fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
error.string());
goto bail;
}
} else if (tag == "uses-package") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
printf("uses-package:'%s'\n",
ResTable::normalizeForOutput(name.string()).string());
@@ -1496,7 +1369,7 @@
goto bail;
}
} else if (tag == "original-package") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
printf("original-package:'%s'\n",
ResTable::normalizeForOutput(name.string()).string());
@@ -1506,7 +1379,7 @@
goto bail;
}
} else if (tag == "supports-gl-texture") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
printf("supports-gl-texture:'%s'\n",
ResTable::normalizeForOutput(name.string()).string());
@@ -1524,9 +1397,9 @@
}
depth--;
} else if (tag == "package-verifier") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
- String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
+ String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error);
if (publicKey != "" && error == "") {
printf("package-verifier: name='%s' publicKey='%s'\n",
ResTable::normalizeForOutput(name.string()).string(),
@@ -1553,35 +1426,38 @@
if (withinApplication) {
if(tag == "activity") {
withinActivity = true;
- activityName = getAttribute(tree, NAME_ATTR, &error);
+ activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
error.string());
goto bail;
}
- activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+ activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
+ &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
error.string());
goto bail;
}
- activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+ activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
+ &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
error.string());
goto bail;
}
- activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
+ activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
+ &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
error.string());
goto bail;
}
- int32_t orien = getResolvedIntegerAttribute(&res, tree,
+ int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
SCREEN_ORIENTATION_ATTR, &error);
if (error == "") {
if (orien == 0 || orien == 6 || orien == 8) {
@@ -1595,21 +1471,21 @@
}
}
} else if (tag == "uses-library") {
- String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
+ String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr,
"ERROR getting 'android:name' attribute for uses-library"
" %s\n", error.string());
goto bail;
}
- int req = getIntegerAttribute(tree,
- REQUIRED_ATTR, NULL, 1);
+ int req = AaptXml::getIntegerAttribute(tree,
+ REQUIRED_ATTR, 1);
printf("uses-library%s:'%s'\n",
req ? "" : "-not-required", ResTable::normalizeForOutput(
libraryName.string()).string());
} else if (tag == "receiver") {
withinReceiver = true;
- receiverName = getAttribute(tree, NAME_ATTR, &error);
+ receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr,
@@ -1618,7 +1494,8 @@
goto bail;
}
- String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
+ String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
+ &error);
if (error == "") {
if (permission == "android.permission.BIND_DEVICE_ADMIN") {
hasBindDeviceAdminPermission = true;
@@ -1629,7 +1506,7 @@
}
} else if (tag == "service") {
withinService = true;
- serviceName = getAttribute(tree, NAME_ATTR, &error);
+ serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:name' attribute for "
@@ -1637,7 +1514,8 @@
goto bail;
}
- String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
+ String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
+ &error);
if (error == "") {
if (permission == "android.permission.BIND_INPUT_METHOD") {
hasBindInputMethodPermission = true;
@@ -1659,22 +1537,24 @@
} else if (tag == "provider") {
withinProvider = true;
- bool exported = getResolvedIntegerAttribute(&res, tree, EXPORTED_ATTR, &error);
+ bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
+ EXPORTED_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
" %s\n", error.string());
goto bail;
}
- bool grantUriPermissions = getResolvedIntegerAttribute(&res, tree,
- GRANT_URI_PERMISSIONS_ATTR, &error);
+ bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
+ res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
" %s\n", error.string());
goto bail;
}
- String8 permission = getResolvedAttribute(&res, tree, PERMISSION_ATTR, &error);
+ String8 permission = AaptXml::getResolvedAttribute(res, tree,
+ PERMISSION_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
" %s\n", error.string());
@@ -1685,7 +1565,8 @@
permission == "android.permission.MANAGE_DOCUMENTS";
} else if (bundle->getIncludeMetaData() && tag == "meta-data") {
- String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
+ String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
+ NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:name' attribute for "
"meta-data:%s\n", error.string());
@@ -1693,12 +1574,12 @@
}
printf("meta-data: name='%s' ",
ResTable::normalizeForOutput(metaDataName.string()).string());
- printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"),
+ printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
&error);
if (error != "") {
// Try looking for a RESOURCE_ATTR
error = "";
- printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR,
+ printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
String8("resource"), &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:value' or "
@@ -1709,7 +1590,7 @@
}
printf("\n");
} else if (withinSupportsInput && tag == "input-type") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
supportedInput.add(name);
} else {
@@ -1721,12 +1602,13 @@
} else if (withinFeatureGroup && tag == "uses-feature") {
FeatureGroup& top = featureGroups.editTop();
- String8 name = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
if (name != "" && error == "") {
top.features.add(name, true);
addParentFeatures(&top, name);
} else {
- int vers = getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error);
+ int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
+ &error);
if (error == "") {
if (vers > top.openGLESVersion) {
top.openGLESVersion = vers;
@@ -1754,7 +1636,7 @@
actCameraSecure = false;
catLauncher = false;
} else if (withinService && tag == "meta-data") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:name' attribute for"
" meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
@@ -1768,7 +1650,8 @@
offHost = false;
}
- String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
+ String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
+ RESOURCE_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:resource' attribute for"
" meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
@@ -1797,7 +1680,7 @@
} else if ((depth == 5) && withinIntentFilter) {
String8 action;
if (tag == "action") {
- action = getAttribute(tree, NAME_ATTR, &error);
+ action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
error.string());
@@ -1849,7 +1732,7 @@
}
if (tag == "category") {
- String8 category = getAttribute(tree, NAME_ATTR, &error);
+ String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
error.string());
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index dd40b20..f24a023b 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -60,9 +60,6 @@
int dumpResources(Bundle* bundle);
-String8 getAttribute(const ResXMLTree& tree, const char* ns,
- const char* attr, String8* outError);
-
status_t writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets,
FILE* fp, bool includeRaw);
#endif // __MAIN_H
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 7979a1d..5deeca2 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -4,6 +4,7 @@
// Build resource files from raw assets.
//
#include "AaptAssets.h"
+#include "AaptXml.h"
#include "CacheUpdater.h"
#include "CrunchCache.h"
#include "FileFinder.h"
@@ -805,6 +806,20 @@
}
}
+ if (bundle->getPlatformBuildVersionCode() != "") {
+ if (!addTagAttribute(root, "", "platformBuildVersionCode",
+ bundle->getPlatformBuildVersionCode(), errorOnFailedInsert, true)) {
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ if (bundle->getPlatformBuildVersionName() != "") {
+ if (!addTagAttribute(root, "", "platformBuildVersionName",
+ bundle->getPlatformBuildVersionName(), errorOnFailedInsert, true)) {
+ return UNKNOWN_ERROR;
+ }
+ }
+
if (bundle->getDebugMode()) {
sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
if (application != NULL) {
@@ -881,6 +896,106 @@
return NO_ERROR;
}
+static int32_t getPlatformAssetCookie(const AssetManager& assets) {
+ // Find the system package (0x01). AAPT always generates attributes
+ // with the type 0x01, so we're looking for the first attribute
+ // resource in the system package.
+ const ResTable& table = assets.getResources(true);
+ Res_value val;
+ ssize_t idx = table.getResource(0x01010000, &val, true);
+ if (idx != NO_ERROR) {
+ // Try as a bag.
+ const ResTable::bag_entry* entry;
+ ssize_t cnt = table.lockBag(0x01010000, &entry);
+ if (cnt >= 0) {
+ idx = entry->stringBlock;
+ }
+ table.unlockBag(entry);
+ }
+
+ if (idx < 0) {
+ return 0;
+ }
+ return table.getTableCookie(idx);
+}
+
+enum {
+ VERSION_CODE_ATTR = 0x0101021b,
+ VERSION_NAME_ATTR = 0x0101021c,
+};
+
+static ssize_t extractPlatformBuildVersion(ResXMLTree& tree, Bundle* bundle) {
+ size_t len;
+ ResXMLTree::event_code_t code;
+ while ((code = tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+ if (code != ResXMLTree::START_TAG) {
+ continue;
+ }
+
+ const char16_t* ctag16 = tree.getElementName(&len);
+ if (ctag16 == NULL) {
+ fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
+ return UNKNOWN_ERROR;
+ }
+
+ String8 tag(ctag16, len);
+ if (tag != "manifest") {
+ continue;
+ }
+
+ String8 error;
+ int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR: failed to get platform version code\n");
+ return UNKNOWN_ERROR;
+ }
+
+ if (versionCode >= 0 && bundle->getPlatformBuildVersionCode() == "") {
+ bundle->setPlatformBuildVersionCode(String8::format("%d", versionCode));
+ }
+
+ String8 versionName = AaptXml::getAttribute(tree, VERSION_NAME_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR: failed to get platform version name\n");
+ return UNKNOWN_ERROR;
+ }
+
+ if (versionName != "" && bundle->getPlatformBuildVersionName() == "") {
+ bundle->setPlatformBuildVersionName(versionName);
+ }
+ return NO_ERROR;
+ }
+
+ fprintf(stderr, "ERROR: no <manifest> tag found in platform AndroidManifest.xml\n");
+ return UNKNOWN_ERROR;
+}
+
+static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) {
+ int32_t cookie = getPlatformAssetCookie(assets);
+ if (cookie == 0) {
+ fprintf(stderr, "ERROR: Platform package not found\n");
+ return UNKNOWN_ERROR;
+ }
+
+ ResXMLTree tree;
+ Asset* asset = assets.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_STREAMING);
+ if (asset == NULL) {
+ fprintf(stderr, "ERROR: Platform AndroidManifest.xml not found\n");
+ return UNKNOWN_ERROR;
+ }
+
+ ssize_t result = NO_ERROR;
+ if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) {
+ fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n");
+ result = UNKNOWN_ERROR;
+ } else {
+ result = extractPlatformBuildVersion(tree, bundle);
+ }
+
+ delete asset;
+ return result;
+}
+
#define ASSIGN_IT(n) \
do { \
ssize_t index = resources->indexOfKey(String8(#n)); \
@@ -1356,6 +1471,17 @@
return UNKNOWN_ERROR;
}
+ // If we're not overriding the platform build versions,
+ // extract them from the platform APK.
+ if (packageType != ResourceTable::System &&
+ (bundle->getPlatformBuildVersionCode() == "" ||
+ bundle->getPlatformBuildVersionName() == "")) {
+ err = extractPlatformBuildVersion(assets->getAssetManager(), bundle);
+ if (err != NO_ERROR) {
+ return UNKNOWN_ERROR;
+ }
+ }
+
const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0));
String8 manifestPath(manifestFile->getPrintableSource());
@@ -2636,13 +2762,14 @@
fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
return -1;
}
- pkg = getAttribute(tree, NULL, "package", NULL);
+ pkg = AaptXml::getAttribute(tree, NULL, "package");
} else if (depth == 2) {
if (tag == "application") {
inApplication = true;
keepTag = true;
- String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android",
+ String8 agent = AaptXml::getAttribute(tree,
+ "http://schemas.android.com/apk/res/android",
"backupAgent", &error);
if (agent.length() > 0) {
addProguardKeepRule(keep, agent, pkg.string(),
@@ -2658,8 +2785,8 @@
}
}
if (keepTag) {
- String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android",
- "name", &error);
+ String8 name = AaptXml::getAttribute(tree,
+ "http://schemas.android.com/apk/res/android", "name", &error);
if (error != "") {
fprintf(stderr, "ERROR: %s\n", error.string());
return -1;