Merge "Make entering recents a lot faster" into mnc-dev
diff --git a/Android.mk b/Android.mk
index d6dac53..4d8a37c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -555,8 +555,6 @@
frameworks/base/core/java/android/service/chooser/ChooserTarget.aidl \
frameworks/base/core/java/android/speech/tts/Voice.aidl \
frameworks/base/core/java/android/app/usage/UsageEvents.aidl \
- frameworks/base/core/java/android/app/AssistStructure.aidl \
- frameworks/base/core/java/android/app/AssistContent.aidl \
frameworks/base/core/java/android/app/Notification.aidl \
frameworks/base/core/java/android/app/NotificationManager.aidl \
frameworks/base/core/java/android/app/WallpaperInfo.aidl \
@@ -566,6 +564,8 @@
frameworks/base/core/java/android/app/AlarmManager.aidl \
frameworks/base/core/java/android/app/SearchableInfo.aidl \
frameworks/base/core/java/android/app/VoiceInteractor.aidl \
+ frameworks/base/core/java/android/app/assist/AssistContent.aidl \
+ frameworks/base/core/java/android/app/assist/AssistStructure.aidl \
frameworks/base/core/java/android/app/job/JobParameters.aidl \
frameworks/base/core/java/android/app/job/JobInfo.aidl \
frameworks/base/core/java/android/appwidget/AppWidgetProviderInfo.aidl \
diff --git a/api/current.txt b/api/current.txt
index 481e651..0467b69 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3445,7 +3445,7 @@
method public void onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder);
method public boolean onPrepareOptionsMenu(android.view.Menu);
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
- method public void onProvideAssistContent(android.app.AssistContent);
+ method public void onProvideAssistContent(android.app.assist.AssistContent);
method public void onProvideAssistData(android.os.Bundle);
method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method protected void onRestart();
@@ -3996,26 +3996,19 @@
field public java.lang.String serviceDetails;
}
- public class AssistContent implements android.os.Parcelable {
+ public deprecated class AssistContent {
ctor public AssistContent();
- method public int describeContents();
method public android.content.ClipData getClipData();
- method public android.content.Intent getIntent();
method public android.net.Uri getWebUri();
method public void setClipData(android.content.ClipData);
method public void setIntent(android.content.Intent);
method public void setWebUri(android.net.Uri);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.app.AssistContent> CREATOR;
}
- public final class AssistStructure implements android.os.Parcelable {
- method public int describeContents();
+ public deprecated class AssistStructure {
+ ctor public AssistStructure();
method public android.content.ComponentName getActivityComponent();
- method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
method public int getWindowNodeCount();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.app.AssistStructure> CREATOR;
}
public static class AssistStructure.ViewNode {
@@ -5900,6 +5893,26 @@
}
+package android.app.assist {
+
+ public final class AssistContent extends android.app.AssistContent implements android.os.Parcelable {
+ ctor public AssistContent(android.os.Parcel);
+ method public int describeContents();
+ method public android.content.Intent getIntent();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.assist.AssistContent> CREATOR;
+ }
+
+ public final class AssistStructure extends android.app.AssistStructure implements android.os.Parcelable {
+ ctor public AssistStructure();
+ method public int describeContents();
+ method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.assist.AssistStructure> CREATOR;
+ }
+
+}
+
package android.app.backup {
public abstract class BackupAgent extends android.content.ContextWrapper {
@@ -28935,7 +28948,7 @@
method public android.view.View onCreateContentView();
method public void onDestroy();
method public boolean[] onGetSupportedCommands(java.lang.String[]);
- method public void onHandleAssist(android.os.Bundle, android.app.AssistStructure, android.app.AssistContent);
+ method public void onHandleAssist(android.os.Bundle, android.app.assist.AssistStructure, android.app.assist.AssistContent);
method public void onHide();
method public boolean onKeyDown(int, android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
@@ -33989,6 +34002,7 @@
method public void setTo(android.util.DisplayMetrics);
method public void setToDefaults();
field public static final int DENSITY_280 = 280; // 0x118
+ field public static final int DENSITY_360 = 360; // 0x168
field public static final int DENSITY_400 = 400; // 0x190
field public static final int DENSITY_560 = 560; // 0x230
field public static final int DENSITY_DEFAULT = 160; // 0xa0
diff --git a/api/system-current.txt b/api/system-current.txt
index 32af01c..d11fe85 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3531,7 +3531,7 @@
method public void onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder);
method public boolean onPrepareOptionsMenu(android.view.Menu);
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
- method public void onProvideAssistContent(android.app.AssistContent);
+ method public void onProvideAssistContent(android.app.assist.AssistContent);
method public void onProvideAssistData(android.os.Bundle);
method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method protected void onRestart();
@@ -4092,26 +4092,19 @@
field public java.lang.String serviceDetails;
}
- public class AssistContent implements android.os.Parcelable {
+ public deprecated class AssistContent {
ctor public AssistContent();
- method public int describeContents();
method public android.content.ClipData getClipData();
- method public android.content.Intent getIntent();
method public android.net.Uri getWebUri();
method public void setClipData(android.content.ClipData);
method public void setIntent(android.content.Intent);
method public void setWebUri(android.net.Uri);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.app.AssistContent> CREATOR;
}
- public final class AssistStructure implements android.os.Parcelable {
- method public int describeContents();
+ public deprecated class AssistStructure {
+ ctor public AssistStructure();
method public android.content.ComponentName getActivityComponent();
- method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
method public int getWindowNodeCount();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.app.AssistStructure> CREATOR;
}
public static class AssistStructure.ViewNode {
@@ -6015,6 +6008,26 @@
}
+package android.app.assist {
+
+ public final class AssistContent extends android.app.AssistContent implements android.os.Parcelable {
+ ctor public AssistContent(android.os.Parcel);
+ method public int describeContents();
+ method public android.content.Intent getIntent();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.assist.AssistContent> CREATOR;
+ }
+
+ public final class AssistStructure extends android.app.AssistStructure implements android.os.Parcelable {
+ ctor public AssistStructure();
+ method public int describeContents();
+ method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.assist.AssistStructure> CREATOR;
+ }
+
+}
+
package android.app.backup {
public abstract class BackupAgent extends android.content.ContextWrapper {
@@ -31068,7 +31081,7 @@
method public android.view.View onCreateContentView();
method public void onDestroy();
method public boolean[] onGetSupportedCommands(java.lang.String[]);
- method public void onHandleAssist(android.os.Bundle, android.app.AssistStructure, android.app.AssistContent);
+ method public void onHandleAssist(android.os.Bundle, android.app.assist.AssistStructure, android.app.assist.AssistContent);
method public void onHide();
method public boolean onKeyDown(int, android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
@@ -36254,6 +36267,7 @@
method public void setTo(android.util.DisplayMetrics);
method public void setToDefaults();
field public static final int DENSITY_280 = 280; // 0x118
+ field public static final int DENSITY_360 = 360; // 0x168
field public static final int DENSITY_400 = 400; // 0x190
field public static final int DENSITY_560 = 560; // 0x230
field public static final int DENSITY_DEFAULT = 160; // 0xa0
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 250b002..cdf7429 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.text.TextUtils;
@@ -39,8 +40,10 @@
(new Telecom()).run(args);
}
- private static final String COMMAND_SET_PHONE_ACOUNT_ENABLED = "set-phone-account-enabled";
- private static final String COMMAND_SET_PHONE_ACOUNT_DISABLED = "set-phone-account-disabled";
+ private static final String COMMAND_SET_PHONE_ACCOUNT_ENABLED = "set-phone-account-enabled";
+ private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled";
+ private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account";
+ private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer";
private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
@@ -54,6 +57,8 @@
"usage: telecom [subcommand] [options]\n" +
"usage: telecom set-phone-account-enabled <COMPONENT> <ID>\n" +
"usage: telecom set-phone-account-disabled <COMPONENT> <ID>\n" +
+ "usage: telecom register-phone-account <COMPONENT> <ID> <LABEL>\n" +
+ "usage: telecom unregister-phone-account <COMPONENT> <ID>\n" +
"usage: telecom set-default-dialer <PACKAGE>\n" +
"usage: telecom get-default-dialer <PACKAGE>\n" +
"\n" +
@@ -63,9 +68,9 @@
"telecom set-phone-account-disabled: Disables the given phone account, if it \n" +
" has already been registered with telecom.\n" +
"\n" +
- "telecom set-default_dialer: Sets the default dialer to the given component. \n" +
+ "telecom set-default-dialer: Sets the default dialer to the given component. \n" +
"\n" +
- "telecom get-default_dialer: Displays the current default dialer. \n"
+ "telecom get-default-dialer: Displays the current default dialer. \n"
);
}
@@ -80,12 +85,18 @@
String command = nextArgRequired();
switch (command) {
- case COMMAND_SET_PHONE_ACOUNT_ENABLED:
+ case COMMAND_SET_PHONE_ACCOUNT_ENABLED:
runSetPhoneAccountEnabled(true);
break;
- case COMMAND_SET_PHONE_ACOUNT_DISABLED:
+ case COMMAND_SET_PHONE_ACCOUNT_DISABLED:
runSetPhoneAccountEnabled(false);
break;
+ case COMMAND_REGISTER_PHONE_ACCOUNT:
+ runRegisterPhoneAccount();
+ break;
+ case COMMAND_UNREGISTER_PHONE_ACCOUNT:
+ runUnregisterPhoneAccount();
+ break;
case COMMAND_SET_DEFAULT_DIALER:
runSetDefaultDialer();
break;
@@ -98,9 +109,7 @@
}
private void runSetPhoneAccountEnabled(boolean enabled) throws RemoteException {
- final ComponentName component = parseComponentName(nextArgRequired());
- final String accountId = nextArgRequired();
- final PhoneAccountHandle handle = new PhoneAccountHandle(component, accountId);
+ final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
final boolean success = mTelecomService.enablePhoneAccount(handle, enabled);
if (success) {
System.out.println("Success - " + handle + (enabled ? " enabled." : " disabled."));
@@ -109,6 +118,21 @@
}
}
+ private void runRegisterPhoneAccount() throws RemoteException {
+ final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
+ final String label = nextArgRequired();
+ PhoneAccount account = PhoneAccount.builder(handle, label)
+ .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER).build();
+ mTelecomService.registerPhoneAccount(account);
+ System.out.println("Success - " + handle + " registered.");
+ }
+
+ private void runUnregisterPhoneAccount() throws RemoteException {
+ final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
+ mTelecomService.unregisterPhoneAccount(handle);
+ System.out.println("Success - " + handle + " unregistered.");
+ }
+
private void runSetDefaultDialer() throws RemoteException {
final String packageName = nextArgRequired();
final boolean success = mTelecomService.setDefaultDialer(packageName);
@@ -124,6 +148,12 @@
System.out.println(mTelecomService.getDefaultDialerPackage());
}
+ private PhoneAccountHandle getPhoneAccountHandleFromArgs() {
+ final ComponentName component = parseComponentName(nextArgRequired());
+ final String accountId = nextArgRequired();
+ return new PhoneAccountHandle(component, accountId);
+ }
+
private ComponentName parseComponentName(String component) {
ComponentName cn = ComponentName.unflattenFromString(component);
if (cn == null) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 073fefc..90567c7 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -38,6 +38,7 @@
import android.annotation.SystemApi;
import android.app.admin.DevicePolicyManager;
+import android.app.assist.AssistContent;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.ContentResolver;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index e4def1e..b6cec60 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -17,6 +17,8 @@
package android.app;
import android.app.ActivityManager.StackInfo;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
import android.content.ComponentName;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2a98b6c..3224d41 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -16,6 +16,8 @@
package android.app;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
import android.app.backup.BackupAgent;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
diff --git a/core/java/android/app/AssistContent.java b/core/java/android/app/AssistContent.java
index f271af1..4cb89a8 100644
--- a/core/java/android/app/AssistContent.java
+++ b/core/java/android/app/AssistContent.java
@@ -26,9 +26,11 @@
/**
* Holds information about the content an application is viewing, to hand to an
* assistant at the user's request. This is filled in by
- * {@link Activity#onProvideAssistContent Activity.onProvideAssistContent}.
+ * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
+ * @deprecated use {@link android.app.assist.AssistContent}.
*/
-public class AssistContent implements Parcelable {
+@Deprecated
+public class AssistContent {
private Intent mIntent;
private ClipData mClipData;
private Uri mUri;
@@ -36,16 +38,16 @@
/**
* @hide
* Key name this data structure is stored in the Bundle generated by
- * {@link Activity#onProvideAssistData}.
+ * {@link android.app.Activity#onProvideAssistData}.
*/
public static final String ASSIST_KEY = "android:assist_content";
/**
* @hide
* Retrieve the framework-generated AssistContent that is stored within
- * the Bundle filled in by {@link Activity#onProvideAssistContent}.
+ * the Bundle filled in by {@link android.app.Activity#onProvideAssistContent}.
*/
- public static AssistContent getAssistContent(Bundle assistBundle) {
+ public static android.app.assist.AssistContent getAssistContent(Bundle assistBundle) {
return assistBundle.getParcelable(ASSIST_KEY);
}
@@ -71,6 +73,7 @@
/**
* Return the current {@link #setIntent}, which you can modify in-place.
+ * @hide
*/
public Intent getIntent() {
return mIntent;
@@ -116,7 +119,8 @@
return mUri;
}
- AssistContent(Parcel in) {
+ /** @hide */
+ public AssistContent(Parcel in) {
if (in.readInt() != 0) {
mIntent = Intent.CREATOR.createFromParcel(in);
}
@@ -128,13 +132,8 @@
}
}
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
if (mIntent != null) {
dest.writeInt(1);
mIntent.writeToParcel(dest, flags);
@@ -154,15 +153,4 @@
dest.writeInt(0);
}
}
-
- public static final Parcelable.Creator<AssistContent> CREATOR
- = new Parcelable.Creator<AssistContent>() {
- public AssistContent createFromParcel(Parcel in) {
- return new AssistContent(in);
- }
-
- public AssistContent[] newArray(int size) {
- return new AssistContent[size];
- }
- };
}
diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java
index ca47a5e..ef7fde4 100644
--- a/core/java/android/app/AssistStructure.java
+++ b/core/java/android/app/AssistStructure.java
@@ -17,9 +17,7 @@
package android.app;
import android.content.ComponentName;
-import android.graphics.Paint;
import android.graphics.Rect;
-import android.graphics.Typeface;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -29,7 +27,6 @@
import android.os.PooledStringWriter;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.text.TextPaint;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -42,19 +39,22 @@
/**
* Assist data automatically created by the platform's implementation
- * of {@link Activity#onProvideAssistData}.
+ * of {@link android.app.Activity#onProvideAssistData}.
+ * @deprecated use {@link android.app.assist.AssistStructure}.
*/
-final public class AssistStructure implements Parcelable {
+@Deprecated
+public class AssistStructure {
static final String TAG = "AssistStructure";
/**
* @hide
* Key name this data structure is stored in the Bundle generated by
- * {@link Activity#onProvideAssistData}.
+ * {@link android.app.Activity#onProvideAssistData}.
*/
public static final String ASSIST_KEY = "android:assist_structure";
- boolean mHaveData;
+ /** @hide */
+ public boolean mHaveData;
ComponentName mActivityComponent;
@@ -62,15 +62,18 @@
final ArrayList<ViewNodeBuilder> mPendingAsyncChildren = new ArrayList<>();
- SendChannel mSendChannel;
- IBinder mReceiveChannel;
+ /** @hide */
+ public SendChannel mSendChannel;
+ /** @hide */
+ public IBinder mReceiveChannel;
Rect mTmpRect = new Rect();
static final int TRANSACTION_XFER = Binder.FIRST_CALL_TRANSACTION+1;
static final String DESCRIPTOR = "android.app.AssistStructure";
- final class SendChannel extends Binder {
+ /** @hide */
+ public final class SendChannel extends Binder {
@Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
if (code == TRANSACTION_XFER) {
@@ -702,7 +705,8 @@
}
}
- AssistStructure(Activity activity) {
+ /** @hide */
+ public AssistStructure(Activity activity) {
mHaveData = true;
mActivityComponent = activity.getComponentName();
ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews(
@@ -713,12 +717,13 @@
}
}
- AssistStructure() {
+ public AssistStructure() {
mHaveData = true;
mActivityComponent = null;
}
- AssistStructure(Parcel in) {
+ /** @hide */
+ public AssistStructure(Parcel in) {
mReceiveChannel = in.readStrongBinder();
}
@@ -792,7 +797,7 @@
* Retrieve the framework-generated AssistStructure that is stored within
* the Bundle filled in by {@link Activity#onProvideAssistData}.
*/
- public static AssistStructure getAssistStructure(Bundle assistBundle) {
+ public static android.app.assist.AssistStructure getAssistStructure(Bundle assistBundle) {
return assistBundle.getParcelable(ASSIST_KEY);
}
@@ -812,16 +817,13 @@
/**
* Return one of the windows in the assist data.
* @param index Which window to retrieve, may be 0 to {@link #getWindowNodeCount()}-1.
+ * @hide
*/
public WindowNode getWindowNodeAt(int index) {
ensureData();
return mWindowNodes.get(index);
}
- public int describeContents() {
- return 0;
- }
-
/** @hide */
public void ensureData() {
if (mHaveData) {
@@ -880,29 +882,4 @@
}
//dump();
}
-
- public void writeToParcel(Parcel out, int flags) {
- if (mHaveData) {
- // This object holds its data. We want to write a send channel that the
- // other side can use to retrieve that data.
- if (mSendChannel == null) {
- mSendChannel = new SendChannel();
- }
- out.writeStrongBinder(mSendChannel);
- } else {
- // This object doesn't hold its data, so just propagate along its receive channel.
- out.writeStrongBinder(mReceiveChannel);
- }
- }
-
- public static final Parcelable.Creator<AssistStructure> CREATOR
- = new Parcelable.Creator<AssistStructure>() {
- public AssistStructure createFromParcel(Parcel in) {
- return new AssistStructure(in);
- }
-
- public AssistStructure[] newArray(int size) {
- return new AssistStructure[size];
- }
- };
}
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 1fb88a9..02e26a5 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -1107,6 +1107,7 @@
}
if (enterTransition != null) {
+ enterTransition.removeTarget(state.nonExistentView);
View view = inFragment.getView();
if (view != null) {
view.captureTransitioningViews(enteringViews);
@@ -1115,7 +1116,6 @@
}
enteringViews.add(state.nonExistentView);
// We added this earlier to prevent any views being targeted.
- enterTransition.removeTarget(state.nonExistentView);
addTargets(enterTransition, enteringViews);
}
setSharedElementEpicenter(enterTransition, state);
@@ -1170,7 +1170,7 @@
Transition exitTransition, Transition sharedElementTransition, Fragment inFragment,
boolean isBack) {
boolean overlap = true;
- if (enterTransition != null && exitTransition != null) {
+ if (enterTransition != null && exitTransition != null && inFragment != null) {
overlap = isBack ? inFragment.getAllowReturnTransitionOverlap() :
inFragment.getAllowEnterTransitionOverlap();
}
@@ -1638,7 +1638,7 @@
private void setBackNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
boolean isEnd) {
- int count = mSharedElementTargetNames.size();
+ int count = mSharedElementTargetNames == null ? 0 : mSharedElementTargetNames.size();
for (int i = 0; i < count; i++) {
String source = mSharedElementSourceNames.get(i);
String originalTarget = mSharedElementTargetNames.get(i);
@@ -1656,7 +1656,7 @@
private void setNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
boolean isEnd) {
- int count = namedViews.size();
+ int count = namedViews == null ? 0 : namedViews.size();
for (int i = 0; i < count; i++) {
String source = namedViews.keyAt(i);
String target = namedViews.valueAt(i).getTransitionName();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 0a425ae..249cdb2 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -19,6 +19,8 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.ActivityManager.StackInfo;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
import android.content.ComponentName;
import android.content.ContentProviderNative;
import android.content.IContentProvider;
diff --git a/core/java/android/app/AssistContent.aidl b/core/java/android/app/assist/AssistContent.aidl
similarity index 95%
rename from core/java/android/app/AssistContent.aidl
rename to core/java/android/app/assist/AssistContent.aidl
index a6321bf..24379bb 100644
--- a/core/java/android/app/AssistContent.aidl
+++ b/core/java/android/app/assist/AssistContent.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.app;
+package android.app.assist;
parcelable AssistContent;
diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java
new file mode 100644
index 0000000..c7e7330
--- /dev/null
+++ b/core/java/android/app/assist/AssistContent.java
@@ -0,0 +1,44 @@
+package android.app.assist;
+
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * New home for AssistContent.
+ */
+public final class AssistContent extends android.app.AssistContent implements Parcelable {
+
+ /** @hide */
+ public AssistContent() {
+ }
+
+ public AssistContent(Parcel in) {
+ super(in);
+ }
+
+ public Intent getIntent() {
+ return super.getIntent();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ public static final Parcelable.Creator<AssistContent> CREATOR
+ = new Parcelable.Creator<AssistContent>() {
+ public AssistContent createFromParcel(Parcel in) {
+ return new AssistContent(in);
+ }
+
+ public AssistContent[] newArray(int size) {
+ return new AssistContent[size];
+ }
+ };
+}
diff --git a/core/java/android/app/AssistStructure.aidl b/core/java/android/app/assist/AssistStructure.aidl
similarity index 95%
rename from core/java/android/app/AssistStructure.aidl
rename to core/java/android/app/assist/AssistStructure.aidl
index 07fb2453..ae0a34c 100644
--- a/core/java/android/app/AssistStructure.aidl
+++ b/core/java/android/app/assist/AssistStructure.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.app;
+package android.app.assist;
parcelable AssistStructure;
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
new file mode 100644
index 0000000..1677e95
--- /dev/null
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -0,0 +1,56 @@
+package android.app.assist;
+
+import android.app.Activity;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * New home for AssistStructure.
+ */
+public final class AssistStructure extends android.app.AssistStructure implements Parcelable {
+
+ public AssistStructure() {
+ }
+
+ /** @hide */
+ public AssistStructure(Activity activity) {
+ super(activity);
+ }
+
+ AssistStructure(Parcel in) {
+ super(in);
+ }
+
+ public WindowNode getWindowNodeAt(int index) {
+ return super.getWindowNodeAt(index);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ if (mHaveData) {
+ // This object holds its data. We want to write a send channel that the
+ // other side can use to retrieve that data.
+ if (mSendChannel == null) {
+ mSendChannel = new SendChannel();
+ }
+ out.writeStrongBinder(mSendChannel);
+ } else {
+ // This object doesn't hold its data, so just propagate along its receive channel.
+ out.writeStrongBinder(mReceiveChannel);
+ }
+ }
+
+ public static final Parcelable.Creator<AssistStructure> CREATOR
+ = new Parcelable.Creator<AssistStructure>() {
+ public AssistStructure createFromParcel(Parcel in) {
+ return new AssistStructure(in);
+ }
+
+ public AssistStructure[] newArray(int size) {
+ return new AssistStructure[size];
+ }
+ };
+}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 17a8eb7..96a80e7 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1552,23 +1552,21 @@
*
* @param uri The URI to watch for changes. This can be a specific row URI, or a base URI
* for a whole class of content.
- * @param notifyForDescendents If <code>true</code> changes to URIs beginning with <code>uri</code>
- * will also cause notifications to be sent. If <code>false</code> only changes to the exact URI
- * specified by <em>uri</em> will cause notifications to be sent. If <code>true</code>, any URI values
- * at or below the specified URI will also trigger a match.
+ * @param notifyForDescendents When false, the observer will be notified whenever a
+ * change occurs to the exact URI specified by <code>uri</code> or to one of the
+ * URI's ancestors in the path hierarchy. When true, the observer will also be notified
+ * whenever a change occurs to the URI's descendants in the path hierarchy.
* @param observer The object that receives callbacks when changes occur.
* @see #unregisterContentObserver
*/
public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
- ContentObserver observer)
- {
+ ContentObserver observer) {
registerContentObserver(uri, notifyForDescendents, observer, UserHandle.myUserId());
}
/** @hide - designated user version */
public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
- ContentObserver observer, int userHandle)
- {
+ ContentObserver observer, int userHandle) {
try {
getContentService().registerContentObserver(uri, notifyForDescendents,
observer.getContentObserver(), userHandle);
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 85e8827..b69ca88 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1022,12 +1022,33 @@
/**
* <p>Position of the camera optical center.</p>
- * <p>As measured in the device sensor coordinate system, the
- * position of the camera device's optical center, as a
- * three-dimensional vector <code>(x,y,z)</code>.</p>
- * <p>To transform a world position to a camera-device centered
- * coordinate system, the position must be translated by this
- * vector and then rotated by {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}.</p>
+ * <p>The position of the camera device's lens optical center,
+ * as a three-dimensional vector <code>(x,y,z)</code>, relative to the
+ * optical center of the largest camera device facing in the
+ * same direction as this camera, in the {@link android.hardware.SensorEvent Android sensor coordinate
+ * axes}. Note that only the axis definitions are shared with
+ * the sensor coordinate system, but not the origin.</p>
+ * <p>If this device is the largest or only camera device with a
+ * given facing, then this position will be <code>(0, 0, 0)</code>; a
+ * camera device with a lens optical center located 3 cm from
+ * the main sensor along the +X axis (to the right from the
+ * user's perspective) will report <code>(0.03, 0, 0)</code>.</p>
+ * <p>To transform a pixel coordinates between two cameras
+ * facing the same direction, first the source camera
+ * android.lens.radialDistortion must be corrected for. Then
+ * the source camera android.lens.intrinsicCalibration needs
+ * to be applied, followed by the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}
+ * of the source camera, the translation of the source camera
+ * relative to the destination camera, the
+ * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination camera, and
+ * finally the inverse of android.lens.intrinsicCalibration
+ * of the destination camera. This obtains a
+ * radial-distortion-free coordinate in the destination
+ * camera pixel coordinates.</p>
+ * <p>To compare this against a real image from the destination
+ * camera, the destination camera image then needs to be
+ * corrected for radial distortion before comparison or
+ * sampling.</p>
* <p><b>Units</b>: Meters</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 0f002a9..0fb6889 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -802,12 +802,9 @@
*/
public ICameraService getCameraService() {
synchronized(mLock) {
+ connectCameraServiceLocked();
if (mCameraService == null) {
- Log.i(TAG, "getCameraService: Reconnecting to camera service");
- connectCameraServiceLocked();
- if (mCameraService == null) {
- Log.e(TAG, "Camera service is unavailable");
- }
+ Log.e(TAG, "Camera service is unavailable");
}
return mCameraService;
}
@@ -815,11 +812,16 @@
/**
* Connect to the camera service if it's available, and set up listeners.
+ * If the service is already connected, do nothing.
*
* <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p>
*/
private void connectCameraServiceLocked() {
- mCameraService = null;
+ // Only reconnect if necessary
+ if (mCameraService != null) return;
+
+ Log.i(TAG, "Connecting to camera service");
+
IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
if (cameraServiceBinder == null) {
// Camera service is now down, leave mCameraService as null
@@ -1098,6 +1100,8 @@
*/
public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
synchronized (mLock) {
+ connectCameraServiceLocked();
+
Handler oldHandler = mCallbackMap.put(callback, handler);
// For new callbacks, provide initial availability information
if (oldHandler == null) {
@@ -1120,6 +1124,8 @@
public void registerTorchCallback(TorchCallback callback, Handler handler) {
synchronized(mLock) {
+ connectCameraServiceLocked();
+
Handler oldHandler = mTorchCallbackMap.put(callback, handler);
// For new callbacks, provide initial torch information
if (oldHandler == null) {
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index df6c986..3bb2fdb 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2584,12 +2584,33 @@
/**
* <p>Position of the camera optical center.</p>
- * <p>As measured in the device sensor coordinate system, the
- * position of the camera device's optical center, as a
- * three-dimensional vector <code>(x,y,z)</code>.</p>
- * <p>To transform a world position to a camera-device centered
- * coordinate system, the position must be translated by this
- * vector and then rotated by {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}.</p>
+ * <p>The position of the camera device's lens optical center,
+ * as a three-dimensional vector <code>(x,y,z)</code>, relative to the
+ * optical center of the largest camera device facing in the
+ * same direction as this camera, in the {@link android.hardware.SensorEvent Android sensor coordinate
+ * axes}. Note that only the axis definitions are shared with
+ * the sensor coordinate system, but not the origin.</p>
+ * <p>If this device is the largest or only camera device with a
+ * given facing, then this position will be <code>(0, 0, 0)</code>; a
+ * camera device with a lens optical center located 3 cm from
+ * the main sensor along the +X axis (to the right from the
+ * user's perspective) will report <code>(0.03, 0, 0)</code>.</p>
+ * <p>To transform a pixel coordinates between two cameras
+ * facing the same direction, first the source camera
+ * android.lens.radialDistortion must be corrected for. Then
+ * the source camera android.lens.intrinsicCalibration needs
+ * to be applied, followed by the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}
+ * of the source camera, the translation of the source camera
+ * relative to the destination camera, the
+ * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination camera, and
+ * finally the inverse of android.lens.intrinsicCalibration
+ * of the destination camera. This obtains a
+ * radial-distortion-free coordinate in the destination
+ * camera pixel coordinates.</p>
+ * <p>To compare this against a real image from the destination
+ * camera, the destination camera image then needs to be
+ * corrected for radial distortion before comparison or
+ * sampling.</p>
* <p><b>Units</b>: Meters</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 16701e5..ad0cd0f 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -906,7 +906,6 @@
}
mRemoteDevice = null;
- mInError = false;
}
}
@@ -1889,13 +1888,13 @@
}
private void checkIfCameraClosedOrInError() throws CameraAccessException {
+ if (mRemoteDevice == null) {
+ throw new IllegalStateException("CameraDevice was already closed");
+ }
if (mInError) {
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
"The camera device has encountered a serious error");
}
- if (mRemoteDevice == null) {
- throw new IllegalStateException("CameraDevice was already closed");
- }
}
/** Whether the camera device has started to close (may not yet have finished) */
diff --git a/core/java/android/os/DropBoxManager.java b/core/java/android/os/DropBoxManager.java
index 27001dc..2b14468 100644
--- a/core/java/android/os/DropBoxManager.java
+++ b/core/java/android/os/DropBoxManager.java
@@ -225,7 +225,8 @@
if ((flags & HAS_BYTE_ARRAY) != 0) {
return new Entry(tag, millis, in.createByteArray(), flags & ~HAS_BYTE_ARRAY);
} else {
- return new Entry(tag, millis, in.readFileDescriptor(), flags);
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.CREATOR.createFromParcel(in);
+ return new Entry(tag, millis, pfd, flags);
}
}
};
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index c24f3fe..1c9c713 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -915,8 +915,6 @@
*/
@Override
public void writeToParcel(Parcel out, int flags) {
- // WARNING: This must stay in sync with Parcel::readParcelFileDescriptor()
- // in frameworks/native/libs/binder/Parcel.cpp
if (mWrapped != null) {
try {
mWrapped.writeToParcel(out, flags);
@@ -924,12 +922,13 @@
releaseResources();
}
} else {
- out.writeFileDescriptor(mFd);
if (mCommFd != null) {
out.writeInt(1);
+ out.writeFileDescriptor(mFd);
out.writeFileDescriptor(mCommFd);
} else {
out.writeInt(0);
+ out.writeFileDescriptor(mFd);
}
if ((flags & PARCELABLE_WRITE_RETURN_VALUE) != 0 && !mClosed) {
// Not a real close, so emit no status
@@ -942,11 +941,10 @@
= new Parcelable.Creator<ParcelFileDescriptor>() {
@Override
public ParcelFileDescriptor createFromParcel(Parcel in) {
- // WARNING: This must stay in sync with Parcel::writeParcelFileDescriptor()
- // in frameworks/native/libs/binder/Parcel.cpp
+ int hasCommChannel = in.readInt();
final FileDescriptor fd = in.readRawFileDescriptor();
FileDescriptor commChannel = null;
- if (in.readInt() != 0) {
+ if (hasCommChannel != 0) {
commChannel = in.readRawFileDescriptor();
}
return new ParcelFileDescriptor(fd, commChannel);
diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl
index 2097d5a..409542d 100644
--- a/core/java/android/security/IKeystoreService.aidl
+++ b/core/java/android/security/IKeystoreService.aidl
@@ -67,7 +67,8 @@
OperationResult begin(IBinder appToken, String alias, int purpose, boolean pruneable,
in KeymasterArguments params, in byte[] entropy);
OperationResult update(IBinder token, in KeymasterArguments params, in byte[] input);
- OperationResult finish(IBinder token, in KeymasterArguments params, in byte[] signature);
+ OperationResult finish(IBinder token, in KeymasterArguments params, in byte[] signature,
+ in byte[] entropy);
int abort(IBinder handle);
boolean isOperationAuthorized(IBinder token);
int addAuthToken(in byte[] authToken);
diff --git a/core/java/android/service/voice/IVoiceInteractionSession.aidl b/core/java/android/service/voice/IVoiceInteractionSession.aidl
index 894edac..8fe84e1 100644
--- a/core/java/android/service/voice/IVoiceInteractionSession.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionSession.aidl
@@ -16,8 +16,8 @@
package android.service.voice;
-import android.app.AssistContent;
-import android.app.AssistStructure;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 48ad5a8..33fef62 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -16,11 +16,11 @@
package android.service.voice;
-import android.app.AssistContent;
-import android.app.AssistStructure;
import android.app.Dialog;
import android.app.Instrumentation;
import android.app.VoiceInteractor;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.Intent;
@@ -386,7 +386,7 @@
}
/**
- * ASk the app to cancel this current request.
+ * ASk the app to cancelLocked this current request.
*/
public void cancel() {
try {
@@ -878,14 +878,34 @@
show(null, 0);
}
- public void show(Bundle args, int showFlags) {
+ /**
+ * Show the UI for this session. This asks the system to go through the process of showing
+ * your UI, which will eventually culminate in {@link #onShow}. This is similar to calling
+ * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
+ * @param args Arbitrary arguments that will be propagated {@link #onShow}.
+ * @param flags Indicates additional optional behavior that should be performed. May
+ * be {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST}
+ * to request that the system generate and deliver assist data on the current foreground
+ * app as part of showing the session UI.
+ */
+ public void show(Bundle args, int flags) {
+ if (mToken == null) {
+ throw new IllegalStateException("Can't call before onCreate()");
+ }
try {
- mSystemService.showSessionFromSession(mToken, null, 0);
+ mSystemService.showSessionFromSession(mToken, args, flags);
} catch (RemoteException e) {
}
}
+ /**
+ * Hide the session's UI, if currently shown. Call this when you are done with your
+ * user interaction.
+ */
public void hide() {
+ if (mToken == null) {
+ throw new IllegalStateException("Can't call before onCreate()");
+ }
try {
mSystemService.hideSessionFromSession(mToken);
} catch (RemoteException e) {
@@ -964,6 +984,9 @@
* {@link #startVoiceActivity}.</p>
*/
public void setKeepAwake(boolean keepAwake) {
+ if (mToken == null) {
+ throw new IllegalStateException("Can't call before onCreate()");
+ }
try {
mSystemService.setKeepAwake(mToken, keepAwake);
} catch (RemoteException e) {
@@ -985,7 +1008,9 @@
}
/**
- * Finish the session.
+ * Finish the session. This completely destroys the session -- the next time it is shown,
+ * an entirely new one will be created. You do not normally call this function; instead,
+ * use {@link #hide} and allow the system to destroy your session if it needs its RAM.
*/
public void finish() {
if (mToken == null) {
@@ -1114,7 +1139,7 @@
* Called when the user presses the back button while focus is in the session UI. Note
* that this will only happen if the session UI has requested input focus in its window;
* otherwise, the back key will go to whatever window has focus and do whatever behavior
- * it normally has there.
+ * it normally has there. The default implementation simply calls {@link #hide}.
*/
public void onBackPressed() {
hide();
@@ -1123,7 +1148,7 @@
/**
* Sessions automatically watch for requests that all system UI be closed (such as when
* the user presses HOME), which will appear here. The default implementation always
- * calls {@link #finish}.
+ * calls {@link #hide}.
*/
public void onCloseSystemDialogs() {
hide();
@@ -1287,7 +1312,7 @@
}
/**
- * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request}
+ * Called when the {@link android.app.VoiceInteractor} has asked to cancelLocked a {@link Request}
* that was previously delivered to {@link #onRequestConfirmation},
* {@link #onRequestPickOption}, {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice},
* or {@link #onRequestCommand}.
diff --git a/core/java/android/text/Annotation.java b/core/java/android/text/Annotation.java
index dbc290b..bb5d3ea 100644
--- a/core/java/android/text/Annotation.java
+++ b/core/java/android/text/Annotation.java
@@ -38,6 +38,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.ANNOTATION;
}
@@ -46,6 +51,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeString(mKey);
dest.writeString(mValue);
}
diff --git a/core/java/android/text/ParcelableSpan.java b/core/java/android/text/ParcelableSpan.java
index 224511a..d7c1a4b 100644
--- a/core/java/android/text/ParcelableSpan.java
+++ b/core/java/android/text/ParcelableSpan.java
@@ -16,6 +16,7 @@
package android.text;
+import android.os.Parcel;
import android.os.Parcelable;
/**
@@ -27,5 +28,21 @@
/**
* Return a special type identifier for this span class.
*/
- public abstract int getSpanTypeId();
+ int getSpanTypeId();
+
+ /**
+ * Internal implementation of {@link #getSpanTypeId()} that is not meant to
+ * be overridden outside of the framework.
+ *
+ * @hide
+ */
+ int getSpanTypeIdInternal();
+
+ /**
+ * Internal implementation of {@link Parcelable#writeToParcel(Parcel, int)}
+ * that is not meant to be overridden outside of the framework.
+ *
+ * @hide
+ */
+ void writeToParcelInternal(Parcel dest, int flags);
}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 676986d..6c4d8fd 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -622,8 +622,7 @@
* Flatten a CharSequence and whatever styles can be copied across processes
* into the parcel.
*/
- public static void writeToParcel(CharSequence cs, Parcel p,
- int parcelableFlags) {
+ public static void writeToParcel(CharSequence cs, Parcel p, int parcelableFlags) {
if (cs instanceof Spanned) {
p.writeInt(0);
p.writeString(cs.toString());
@@ -645,15 +644,15 @@
}
if (prop instanceof ParcelableSpan) {
- ParcelableSpan ps = (ParcelableSpan)prop;
- int spanTypeId = ps.getSpanTypeId();
+ final ParcelableSpan ps = (ParcelableSpan) prop;
+ final int spanTypeId = ps.getSpanTypeIdInternal();
if (spanTypeId < FIRST_SPAN || spanTypeId > LAST_SPAN) {
- Log.e(TAG, "external class \"" + ps.getClass().getSimpleName()
+ Log.e(TAG, "External class \"" + ps.getClass().getSimpleName()
+ "\" is attempting to use the frameworks-only ParcelableSpan"
+ " interface");
} else {
p.writeInt(spanTypeId);
- ps.writeToParcel(p, parcelableFlags);
+ ps.writeToParcelInternal(p, parcelableFlags);
writeWhere(p, sp, o);
}
}
diff --git a/core/java/android/text/style/AbsoluteSizeSpan.java b/core/java/android/text/style/AbsoluteSizeSpan.java
index 1214040..908ef55 100644
--- a/core/java/android/text/style/AbsoluteSizeSpan.java
+++ b/core/java/android/text/style/AbsoluteSizeSpan.java
@@ -49,6 +49,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.ABSOLUTE_SIZE_SPAN;
}
@@ -57,6 +62,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mSize);
dest.writeInt(mDip ? 1 : 0);
}
diff --git a/core/java/android/text/style/AlignmentSpan.java b/core/java/android/text/style/AlignmentSpan.java
index b8a37da..6158309 100644
--- a/core/java/android/text/style/AlignmentSpan.java
+++ b/core/java/android/text/style/AlignmentSpan.java
@@ -22,10 +22,9 @@
import android.text.TextUtils;
public interface AlignmentSpan extends ParagraphStyle {
- public Layout.Alignment getAlignment();
+ Layout.Alignment getAlignment();
- public static class Standard
- implements AlignmentSpan, ParcelableSpan {
+ class Standard implements AlignmentSpan, ParcelableSpan {
public Standard(Layout.Alignment align) {
mAlignment = align;
}
@@ -35,6 +34,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.ALIGNMENT_SPAN;
}
@@ -43,6 +47,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeString(mAlignment.name());
}
diff --git a/core/java/android/text/style/BackgroundColorSpan.java b/core/java/android/text/style/BackgroundColorSpan.java
index cda8015..de05f50 100644
--- a/core/java/android/text/style/BackgroundColorSpan.java
+++ b/core/java/android/text/style/BackgroundColorSpan.java
@@ -35,6 +35,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.BACKGROUND_COLOR_SPAN;
}
@@ -43,6 +48,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mColor);
}
diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java
index 3f86b08..7408415 100644
--- a/core/java/android/text/style/BulletSpan.java
+++ b/core/java/android/text/style/BulletSpan.java
@@ -60,6 +60,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.BULLET_SPAN;
}
@@ -68,6 +73,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mGapWidth);
dest.writeInt(mWantColor ? 1 : 0);
dest.writeInt(mColor);
diff --git a/core/java/android/text/style/EasyEditSpan.java b/core/java/android/text/style/EasyEditSpan.java
index 03b4f60..7af1c2c 100644
--- a/core/java/android/text/style/EasyEditSpan.java
+++ b/core/java/android/text/style/EasyEditSpan.java
@@ -91,12 +91,22 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeParcelable(mPendingIntent, 0);
dest.writeByte((byte) (mDeleteEnabled ? 1 : 0));
}
@Override
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.EASY_EDIT_SPAN;
}
diff --git a/core/java/android/text/style/ForegroundColorSpan.java b/core/java/android/text/style/ForegroundColorSpan.java
index f167aab..2bc6d54 100644
--- a/core/java/android/text/style/ForegroundColorSpan.java
+++ b/core/java/android/text/style/ForegroundColorSpan.java
@@ -36,14 +36,24 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.FOREGROUND_COLOR_SPAN;
}
-
+
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mColor);
}
diff --git a/core/java/android/text/style/LeadingMarginSpan.java b/core/java/android/text/style/LeadingMarginSpan.java
index 96a7cd9..339d885 100644
--- a/core/java/android/text/style/LeadingMarginSpan.java
+++ b/core/java/android/text/style/LeadingMarginSpan.java
@@ -125,6 +125,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.LEADING_MARGIN_SPAN;
}
@@ -133,6 +138,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mFirst);
dest.writeInt(mRest);
}
diff --git a/core/java/android/text/style/LocaleSpan.java b/core/java/android/text/style/LocaleSpan.java
index a12c42f..d286231 100644
--- a/core/java/android/text/style/LocaleSpan.java
+++ b/core/java/android/text/style/LocaleSpan.java
@@ -44,6 +44,11 @@
@Override
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.LOCALE_SPAN;
}
@@ -54,6 +59,11 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeString(mLocale.getLanguage());
dest.writeString(mLocale.getCountry());
dest.writeString(mLocale.getVariant());
diff --git a/core/java/android/text/style/QuoteSpan.java b/core/java/android/text/style/QuoteSpan.java
index 17748ca..0b0a82c 100644
--- a/core/java/android/text/style/QuoteSpan.java
+++ b/core/java/android/text/style/QuoteSpan.java
@@ -45,6 +45,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.QUOTE_SPAN;
}
@@ -53,6 +58,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mColor);
}
diff --git a/core/java/android/text/style/RelativeSizeSpan.java b/core/java/android/text/style/RelativeSizeSpan.java
index 632dbd4..95f048a 100644
--- a/core/java/android/text/style/RelativeSizeSpan.java
+++ b/core/java/android/text/style/RelativeSizeSpan.java
@@ -34,6 +34,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.RELATIVE_SIZE_SPAN;
}
@@ -42,6 +47,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeFloat(mProportion);
}
diff --git a/core/java/android/text/style/ScaleXSpan.java b/core/java/android/text/style/ScaleXSpan.java
index a22a5a1..d0850185 100644
--- a/core/java/android/text/style/ScaleXSpan.java
+++ b/core/java/android/text/style/ScaleXSpan.java
@@ -34,6 +34,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.SCALE_X_SPAN;
}
@@ -42,6 +47,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeFloat(mProportion);
}
diff --git a/core/java/android/text/style/SpellCheckSpan.java b/core/java/android/text/style/SpellCheckSpan.java
index 0d8a103..10275c2 100644
--- a/core/java/android/text/style/SpellCheckSpan.java
+++ b/core/java/android/text/style/SpellCheckSpan.java
@@ -54,11 +54,21 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mSpellCheckInProgress ? 1 : 0);
}
@Override
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.SPELL_CHECK_SPAN;
}
}
diff --git a/core/java/android/text/style/StrikethroughSpan.java b/core/java/android/text/style/StrikethroughSpan.java
index 303e41574..1389704 100644
--- a/core/java/android/text/style/StrikethroughSpan.java
+++ b/core/java/android/text/style/StrikethroughSpan.java
@@ -30,6 +30,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.STRIKETHROUGH_SPAN;
}
@@ -38,6 +43,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
}
@Override
diff --git a/core/java/android/text/style/StyleSpan.java b/core/java/android/text/style/StyleSpan.java
index b08f70e..f900db5 100644
--- a/core/java/android/text/style/StyleSpan.java
+++ b/core/java/android/text/style/StyleSpan.java
@@ -50,6 +50,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.STYLE_SPAN;
}
@@ -58,6 +63,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mStyle);
}
diff --git a/core/java/android/text/style/SubscriptSpan.java b/core/java/android/text/style/SubscriptSpan.java
index de1d8b2..f1b0d38 100644
--- a/core/java/android/text/style/SubscriptSpan.java
+++ b/core/java/android/text/style/SubscriptSpan.java
@@ -29,6 +29,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.SUBSCRIPT_SPAN;
}
@@ -37,6 +42,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
}
@Override
diff --git a/core/java/android/text/style/SuggestionRangeSpan.java b/core/java/android/text/style/SuggestionRangeSpan.java
index 2dbfc72..c1943d5 100644
--- a/core/java/android/text/style/SuggestionRangeSpan.java
+++ b/core/java/android/text/style/SuggestionRangeSpan.java
@@ -46,11 +46,21 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mBackgroundColor);
}
@Override
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.SUGGESTION_RANGE_SPAN;
}
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index 8b40953..6b449f9 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -248,6 +248,11 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeStringArray(mSuggestions);
dest.writeInt(mFlags);
dest.writeString(mLocaleString);
@@ -264,6 +269,11 @@
@Override
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.SUGGESTION_SPAN;
}
diff --git a/core/java/android/text/style/SuperscriptSpan.java b/core/java/android/text/style/SuperscriptSpan.java
index 285fe84..abcf688 100644
--- a/core/java/android/text/style/SuperscriptSpan.java
+++ b/core/java/android/text/style/SuperscriptSpan.java
@@ -29,6 +29,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.SUPERSCRIPT_SPAN;
}
@@ -37,6 +42,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
}
@Override
diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java
index ecbf4bc..abbd793 100644
--- a/core/java/android/text/style/TextAppearanceSpan.java
+++ b/core/java/android/text/style/TextAppearanceSpan.java
@@ -136,6 +136,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.TEXT_APPEARANCE_SPAN;
}
@@ -144,6 +149,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeString(mTypeface);
dest.writeInt(mStyle);
dest.writeInt(mTextSize);
diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java
index 342a183..c40f11f 100644
--- a/core/java/android/text/style/TtsSpan.java
+++ b/core/java/android/text/style/TtsSpan.java
@@ -495,12 +495,22 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeString(mType);
dest.writePersistableBundle(mArgs);
}
@Override
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.TTS_SPAN;
}
diff --git a/core/java/android/text/style/TypefaceSpan.java b/core/java/android/text/style/TypefaceSpan.java
index f194060..aa622d8 100644
--- a/core/java/android/text/style/TypefaceSpan.java
+++ b/core/java/android/text/style/TypefaceSpan.java
@@ -42,6 +42,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.TYPEFACE_SPAN;
}
@@ -50,6 +55,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeString(mFamily);
}
diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java
index 0669b6f..58239ef 100644
--- a/core/java/android/text/style/URLSpan.java
+++ b/core/java/android/text/style/URLSpan.java
@@ -40,6 +40,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.URL_SPAN;
}
@@ -48,6 +53,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeString(mURL);
}
diff --git a/core/java/android/text/style/UnderlineSpan.java b/core/java/android/text/style/UnderlineSpan.java
index 80b2427..9024dcd 100644
--- a/core/java/android/text/style/UnderlineSpan.java
+++ b/core/java/android/text/style/UnderlineSpan.java
@@ -30,6 +30,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.UNDERLINE_SPAN;
}
@@ -38,6 +43,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
}
@Override
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index d0e5b9e..a36e66c 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -79,6 +79,14 @@
* This is not a density that applications should target, instead relying
* on the system to scale their {@link #DENSITY_XXHIGH} assets for them.
*/
+ public static final int DENSITY_360 = 360;
+
+ /**
+ * Intermediate density for screens that sit somewhere between
+ * {@link #DENSITY_XHIGH} (320 dpi) and {@link #DENSITY_XXHIGH} (480 dpi).
+ * This is not a density that applications should target, instead relying
+ * on the system to scale their {@link #DENSITY_XXHIGH} assets for them.
+ */
public static final int DENSITY_400 = 400;
/**
diff --git a/core/java/android/util/StateSet.java b/core/java/android/util/StateSet.java
index 83dfc47..c2a6a7a 100644
--- a/core/java/android/util/StateSet.java
+++ b/core/java/android/util/StateSet.java
@@ -119,7 +119,14 @@
/** @hide */
public StateSet() {}
+ /**
+ * A state specification that will be matched by all StateSets.
+ */
public static final int[] WILD_CARD = new int[0];
+
+ /**
+ * A state set that does not contain any valid states.
+ */
public static final int[] NOTHING = new int[] { 0 };
/**
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 886547a..e525474 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -18,7 +18,6 @@
import android.graphics.Rect;
import android.os.Bundle;
-import android.text.TextPaint;
/**
* Container for storing additional per-view data generated by {@link View#onProvideStructure
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 9e0719d..997e7e8 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -251,7 +251,7 @@
* @return true if any inset values are nonzero
*/
public boolean hasInsets() {
- return hasSystemWindowInsets() || hasWindowDecorInsets();
+ return hasSystemWindowInsets() || hasWindowDecorInsets() || hasStableInsets();
}
/**
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 7f85f5a..a0d1930 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -5309,13 +5309,6 @@
}
@Override
- void syncSelectedItem() {
- if (mDataChanged) {
- layoutChildren();
- }
- }
-
- @Override
protected void handleDataChanged() {
int count = mItemCount;
int lastHandledItemCount = mLastHandledItemCount;
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index cfe02bd..6962711 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -551,67 +551,37 @@
}
/**
- * Returns the position of the currently selected item within the adapter's
- * data set, or {@link #INVALID_POSITION} if there is nothing selected.
- * <p>
- * <strong>Note:</strong> Prior to {@link android.os.Build.VERSION_CODES#MNC},
- * calling this method between an adapter data set change and a subsequent
- * layout pass could return invalid data.
+ * Return the position of the currently selected item within the adapter's data set
*
- * @return the selected item's position (starting at 0), or
- * {@link #INVALID_POSITION} if there is nothing selected
+ * @return int Position (starting at 0), or {@link #INVALID_POSITION} if there is nothing selected.
*/
@ViewDebug.CapturedViewProperty
public int getSelectedItemPosition() {
- syncSelectedItem();
return mNextSelectedPosition;
}
/**
- * Returns the row ID corresponding to the currently selected item, or
- * {@link #INVALID_ROW_ID} if nothing is selected.
- * <p>
- * <strong>Note:</strong> Prior to {@link android.os.Build.VERSION_CODES#MNC},
- * calling this method between an adapter data set change and a subsequent
- * layout pass could return invalid data.
- *
- * @return the selected item's row ID, or {@link #INVALID_ROW_ID} if
- * nothing is selected
+ * @return The id corresponding to the currently selected item, or {@link #INVALID_ROW_ID}
+ * if nothing is selected.
*/
@ViewDebug.CapturedViewProperty
public long getSelectedItemId() {
- syncSelectedItem();
return mNextSelectedRowId;
}
/**
- * Returns the view corresponding to the currently selected item, or
- * {@code null} if nothing is selected.
- * <p>
- * <strong>Note:</strong> Prior to {@link android.os.Build.VERSION_CODES#MNC},
- * calling this method between an adapter data set change and a subsequent
- * layout pass could return inconsistent data.
- *
- * @return the selected item's view, or {@code null} if nothing is selected
+ * @return The view corresponding to the currently selected item, or null
+ * if nothing is selected
*/
- @Nullable
public abstract View getSelectedView();
/**
- * Returns the data corresponding to the currently selected item, or
- * {@code null} if nothing is selected.
- * <p>
- * <strong>Note:</strong> Prior to {@link android.os.Build.VERSION_CODES#MNC},
- * calling this method between an adapter data set change and a subsequent
- * layout pass could return inconsistent data.
- *
- * @return the data corresponding to the currently selected item, or
- * {@code null} if there is nothing selected.
+ * @return The data corresponding to the currently selected item, or
+ * null if there is nothing selected.
*/
- @Nullable
public Object getSelectedItem() {
- final T adapter = getAdapter();
- final int selection = getSelectedItemPosition();
+ T adapter = getAdapter();
+ int selection = getSelectedItemPosition();
if (adapter != null && adapter.getCount() > 0 && selection >= 0) {
return adapter.getItem(selection);
} else {
@@ -620,15 +590,6 @@
}
/**
- * Synchronizes the selected item's position and ID, if necessary.
- */
- void syncSelectedItem() {
- if (mDataChanged) {
- onLayout(false, mLeft, mTop, mRight, mBottom);
- }
- }
-
- /**
* @return The number of items owned by the Adapter associated with this
* AdapterView. (This is the number of data items, which may be
* larger than the number of visible views.)
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index ce94727..e607a3f 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -20,6 +20,7 @@
import android.content.pm.PackageManager;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Printer;
import android.util.Slog;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
@@ -314,6 +315,15 @@
}
return null;
}
+
+ protected void dump(final Printer pw, final String prefix) {
+ final int N = mImeSubtypeList.size();
+ for (int i = 0; i < N; ++i) {
+ final int rank = i;
+ final ImeSubtypeListItem item = mImeSubtypeList.get(i);
+ pw.println(prefix + "rank=" + rank + " item=" + item);
+ }
+ }
}
private static class DynamicRotationList {
@@ -388,6 +398,14 @@
}
return null;
}
+
+ protected void dump(final Printer pw, final String prefix) {
+ for (int i = 0; i < mUsageHistoryOfSubtypeListItemIndex.length; ++i) {
+ final int rank = mUsageHistoryOfSubtypeListItemIndex[i];
+ final ImeSubtypeListItem item = mImeSubtypeList.get(i);
+ pw.println(prefix + "rank=" + rank + " item=" + item);
+ }
+ }
}
@VisibleForTesting
@@ -478,6 +496,13 @@
}
return result;
}
+
+ protected void dump(final Printer pw) {
+ pw.println(" mSwitchingAwareRotationList:");
+ mSwitchingAwareRotationList.dump(pw, " ");
+ pw.println(" mSwitchingUnawareRotationList:");
+ mSwitchingUnawareRotationList.dump(pw, " ");
+ }
}
private final InputMethodSettings mSettings;
@@ -526,4 +551,12 @@
return mSubtypeList.getSortedInputMethodAndSubtypeList(
showSubtypes, includingAuxiliarySubtypes, isScreenLocked);
}
+
+ public void dump(final Printer pw) {
+ if (mController != null) {
+ mController.dump(pw);
+ } else {
+ pw.println(" mController=null");
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index c32ba85..c77d614 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -84,7 +84,7 @@
private final Rect mContentRect = new Rect();
private Menu mMenu;
- private List<CharSequence> mShowingTitles = new ArrayList<CharSequence>();
+ private List<Object> mShowingMenuItems = new ArrayList<Object>();
private MenuItem.OnMenuItemClickListener mMenuItemClickListener = NO_OP_MENUITEM_CLICK_LISTENER;
private int mSuggestedWidth;
@@ -156,7 +156,7 @@
if (!isCurrentlyShowing(menuItems) || mWidthChanged) {
mPopup.dismiss();
mPopup.layoutMenuItems(menuItems, mMenuItemClickListener, mSuggestedWidth);
- mShowingTitles = getMenuItemTitles(menuItems);
+ mShowingMenuItems = getShowingMenuItemsReferences(menuItems);
}
mPopup.updateCoordinates(mContentRect);
if (!mPopup.isShowing()) {
@@ -211,7 +211,7 @@
* Returns true if this floating toolbar is currently showing the specified menu items.
*/
private boolean isCurrentlyShowing(List<MenuItem> menuItems) {
- return mShowingTitles.equals(getMenuItemTitles(menuItems));
+ return mShowingMenuItems.equals(getShowingMenuItemsReferences(menuItems));
}
/**
@@ -234,12 +234,16 @@
return menuItems;
}
- private List<CharSequence> getMenuItemTitles(List<MenuItem> menuItems) {
- List<CharSequence> titles = new ArrayList<CharSequence>();
+ private List<Object> getShowingMenuItemsReferences(List<MenuItem> menuItems) {
+ List<Object> references = new ArrayList<Object>();
for (MenuItem menuItem : menuItems) {
- titles.add(menuItem.getTitle());
+ if (isIconOnlyMenuItem(menuItem)) {
+ references.add(menuItem.getIcon());
+ } else {
+ references.add(menuItem.getTitle());
+ }
}
- return titles;
+ return references;
}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 37ed723..1a05104 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -514,15 +514,20 @@
}
}
- public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] signature) {
+ public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] signature,
+ byte[] entropy) {
try {
- return mBinder.finish(token, arguments, signature);
+ return mBinder.finish(token, arguments, signature, entropy);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
}
}
+ public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] signature) {
+ return finish(token, arguments, signature, null);
+ }
+
public int abort(IBinder token) {
try {
return mBinder.abort(token);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
index aa2b946..e555cc0 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
@@ -122,6 +122,106 @@
PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA512AndMGF1Padding");
put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-512AndMGF1Padding",
"RSA/ECB/OAEPWithSHA-512AndMGF1Padding");
+
+ // --------------------- java.security.Signature
+ putSignatureImpl("NONEwithRSA",
+ PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$NONEWithPKCS1Padding");
+
+ putSignatureImpl("MD5withRSA",
+ PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$MD5WithPKCS1Padding");
+ put("Alg.Alias.Signature.MD5WithRSAEncryption", "MD5WithRSA");
+ put("Alg.Alias.Signature.MD5/RSA", "MD5WithRSA");
+ put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5WithRSA");
+ put("Alg.Alias.Signature.1.2.840.113549.2.5with1.2.840.113549.1.1.1", "MD5WithRSA");
+
+ putSignatureImpl("SHA1withRSA",
+ PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA1WithPKCS1Padding");
+ put("Alg.Alias.Signature.SHA1WithRSAEncryption", "SHA1WithRSA");
+ put("Alg.Alias.Signature.SHA1/RSA", "SHA1WithRSA");
+ put("Alg.Alias.Signature.SHA-1/RSA", "SHA1WithRSA");
+ put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1WithRSA");
+ put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.1", "SHA1WithRSA");
+ put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.5", "SHA1WithRSA");
+ put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1WithRSA");
+
+ putSignatureImpl("SHA224withRSA",
+ PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA224WithPKCS1Padding");
+ put("Alg.Alias.Signature.SHA224WithRSAEncryption", "SHA224WithRSA");
+ put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA224WithRSA");
+ put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.113549.1.1.1",
+ "SHA224WithRSA");
+ put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.113549.1.1.11",
+ "SHA224WithRSA");
+
+ putSignatureImpl("SHA256withRSA",
+ PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA256WithPKCS1Padding");
+ put("Alg.Alias.Signature.SHA256WithRSAEncryption", "SHA256WithRSA");
+ put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256WithRSA");
+ put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.113549.1.1.1",
+ "SHA256WithRSA");
+ put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.113549.1.1.11",
+ "SHA256WithRSA");
+
+ putSignatureImpl("SHA384withRSA",
+ PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA384WithPKCS1Padding");
+ put("Alg.Alias.Signature.SHA384WithRSAEncryption", "SHA384WithRSA");
+ put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384WithRSA");
+ put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.2with1.2.840.113549.1.1.1",
+ "SHA384WithRSA");
+
+ putSignatureImpl("SHA512withRSA",
+ PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA512WithPKCS1Padding");
+ put("Alg.Alias.Signature.SHA512WithRSAEncryption", "SHA512WithRSA");
+ put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512WithRSA");
+ put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.3with1.2.840.113549.1.1.1",
+ "SHA512WithRSA");
+
+ putSignatureImpl("SHA1withRSA/PSS",
+ PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA1WithPSSPadding");
+ putSignatureImpl("SHA224withRSA/PSS",
+ PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA224WithPSSPadding");
+ putSignatureImpl("SHA256withRSA/PSS",
+ PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA256WithPSSPadding");
+ putSignatureImpl("SHA384withRSA/PSS",
+ PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA384WithPSSPadding");
+ putSignatureImpl("SHA512withRSA/PSS",
+ PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA512WithPSSPadding");
+
+ putSignatureImpl("NONEwithECDSA",
+ PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$NONE");
+
+ putSignatureImpl("ECDSA", PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA1");
+ put("Alg.Alias.Signature.SHA1withECDSA", "ECDSA");
+ put("Alg.Alias.Signature.ECDSAwithSHA1", "ECDSA");
+ // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA1(1)
+ put("Alg.Alias.Signature.1.2.840.10045.4.1", "ECDSA");
+ put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10045.2.1", "ECDSA");
+
+ // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA2(3)
+ putSignatureImpl("SHA224withECDSA",
+ PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA224");
+ // ecdsa-with-SHA224(1)
+ put("Alg.Alias.Signature.1.2.840.10045.4.3.1", "SHA224withECDSA");
+ put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.10045.2.1", "SHA224withECDSA");
+
+ // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA2(3)
+ putSignatureImpl("SHA256withECDSA",
+ PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA256");
+ // ecdsa-with-SHA256(2)
+ put("Alg.Alias.Signature.1.2.840.10045.4.3.2", "SHA256withECDSA");
+ put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.10045.2.1", "SHA256withECDSA");
+
+ putSignatureImpl("SHA384withECDSA",
+ PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA384");
+ // ecdsa-with-SHA384(3)
+ put("Alg.Alias.Signature.1.2.840.10045.4.3.3", "SHA384withECDSA");
+ put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.2with1.2.840.10045.2.1", "SHA384withECDSA");
+
+ putSignatureImpl("SHA512withECDSA",
+ PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA512");
+ // ecdsa-with-SHA512(4)
+ put("Alg.Alias.Signature.1.2.840.10045.4.3.4", "SHA512withECDSA");
+ put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.3with1.2.840.10045.2.1", "SHA512withECDSA");
}
private void putMacImpl(String algorithm, String implClass) {
@@ -139,4 +239,10 @@
put("Cipher." + transformation + " SupportedKeyClasses",
KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
}
+
+ private void putSignatureImpl(String algorithm, String implClass) {
+ put("Signature." + algorithm, implClass);
+ put("Signature." + algorithm + " SupportedKeyClasses",
+ KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
+ }
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
new file mode 100644
index 0000000..335da07
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import android.annotation.NonNull;
+import android.security.KeyStore;
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+
+import java.security.InvalidKeyException;
+import java.security.SignatureSpi;
+
+/**
+ * Base class for {@link SignatureSpi} providing Android KeyStore backed ECDSA signatures.
+ *
+ * @hide
+ */
+abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignatureSpiBase {
+
+ public final static class NONE extends AndroidKeyStoreECDSASignatureSpi {
+ public NONE() {
+ super(KeymasterDefs.KM_DIGEST_NONE);
+ }
+ }
+
+ public final static class SHA1 extends AndroidKeyStoreECDSASignatureSpi {
+ public SHA1() {
+ super(KeymasterDefs.KM_DIGEST_SHA1);
+ }
+ }
+
+ public final static class SHA224 extends AndroidKeyStoreECDSASignatureSpi {
+ public SHA224() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_224);
+ }
+ }
+
+ public final static class SHA256 extends AndroidKeyStoreECDSASignatureSpi {
+ public SHA256() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_256);
+ }
+ }
+
+ public final static class SHA384 extends AndroidKeyStoreECDSASignatureSpi {
+ public SHA384() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_384);
+ }
+ }
+
+ public final static class SHA512 extends AndroidKeyStoreECDSASignatureSpi {
+ public SHA512() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_512);
+ }
+ }
+
+ private final int mKeymasterDigest;
+
+ private int mGroupSizeBytes = -1;
+
+ AndroidKeyStoreECDSASignatureSpi(int keymasterDigest) {
+ mKeymasterDigest = keymasterDigest;
+ }
+
+ @Override
+ protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
+ if (!KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(key.getAlgorithm())) {
+ throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
+ + ". Only" + KeyProperties.KEY_ALGORITHM_EC + " supported");
+ }
+
+ KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
+ int errorCode = getKeyStore().getKeyCharacteristics(
+ key.getAlias(), null, null, keyCharacteristics);
+ if (errorCode != KeyStore.NO_ERROR) {
+ throw getKeyStore().getInvalidKeyException(key.getAlias(), errorCode);
+ }
+ int keySizeBits = keyCharacteristics.getInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
+ if (keySizeBits == -1) {
+ throw new InvalidKeyException("Size of key not known");
+ }
+ mGroupSizeBytes = (keySizeBits + 7) / 8;
+
+ super.initKey(key);
+ }
+
+ @Override
+ protected final void resetAll() {
+ mGroupSizeBytes = -1;
+ super.resetAll();
+ }
+
+ @Override
+ protected final void resetWhilePreservingInitState() {
+ super.resetWhilePreservingInitState();
+ }
+
+ @Override
+ protected void addAlgorithmSpecificParametersToBegin(
+ @NonNull KeymasterArguments keymasterArgs) {
+ keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_EC);
+ keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
+ }
+
+ @Override
+ protected int getAdditionalEntropyAmountForBegin() {
+ return (isSigning()) ? mGroupSizeBytes : 0;
+ }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java
new file mode 100644
index 0000000..3ed396d
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+
+/**
+ * {@link ECPublicKey} backed by keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreECPublicKey extends AndroidKeyStorePublicKey implements ECPublicKey {
+
+ private final ECParameterSpec mParams;
+ private final ECPoint mW;
+
+ public AndroidKeyStoreECPublicKey(String alias, byte[] x509EncodedForm, ECParameterSpec params,
+ ECPoint w) {
+ super(alias, KeyProperties.KEY_ALGORITHM_EC, x509EncodedForm);
+ mParams = params;
+ mW = w;
+ }
+
+ public AndroidKeyStoreECPublicKey(String alias, ECPublicKey info) {
+ this(alias, info.getEncoded(), info.getParams(), info.getW());
+ if (!"X.509".equalsIgnoreCase(info.getFormat())) {
+ throw new IllegalArgumentException(
+ "Unsupported key export format: " + info.getFormat());
+ }
+ }
+
+ @Override
+ public ECParameterSpec getParams() {
+ return mParams;
+ }
+
+ @Override
+ public ECPoint getW() {
+ return mW;
+ }
+}
\ No newline at end of file
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index dc4c8a3..4d6178f 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -296,19 +296,33 @@
int flags = 0;
String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias();
KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
- int errorCode = mKeyStore.generateKey(
- keyAliasInKeystore, args, additionalEntropy, flags, resultingKeyCharacteristics);
- if (errorCode != KeyStore.NO_ERROR) {
- throw new ProviderException(
- "Keystore operation failed", KeyStore.getKeyStoreException(errorCode));
- }
- @KeyProperties.KeyAlgorithmEnum String keyAlgorithmJCA;
+ boolean success = false;
try {
- keyAlgorithmJCA = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
- mKeymasterAlgorithm, mKeymasterDigest);
- } catch (IllegalArgumentException e) {
- throw new ProviderException("Failed to obtain JCA secret key algorithm name", e);
+ Credentials.deleteAllTypesForAlias(mKeyStore, spec.getKeystoreAlias());
+ int errorCode = mKeyStore.generateKey(
+ keyAliasInKeystore,
+ args,
+ additionalEntropy,
+ flags,
+ resultingKeyCharacteristics);
+ if (errorCode != KeyStore.NO_ERROR) {
+ throw new ProviderException(
+ "Keystore operation failed", KeyStore.getKeyStoreException(errorCode));
+ }
+ @KeyProperties.KeyAlgorithmEnum String keyAlgorithmJCA;
+ try {
+ keyAlgorithmJCA = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
+ mKeymasterAlgorithm, mKeymasterDigest);
+ } catch (IllegalArgumentException e) {
+ throw new ProviderException("Failed to obtain JCA secret key algorithm name", e);
+ }
+ SecretKey result = new AndroidKeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA);
+ success = true;
+ return result;
+ } finally {
+ if (!success) {
+ Credentials.deleteAllTypesForAlias(mKeyStore, spec.getKeystoreAlias());
+ }
}
- return new AndroidKeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA);
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 4b45fd7..7b5ca3a 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -121,7 +121,6 @@
public KeyPair generateKeyPair() {
if (mKeyStore == null || mSpec == null) {
throw new IllegalStateException("Not initialized");
-
}
final int flags = (mEncryptionAtRestRequired) ? KeyStore.FLAG_ENCRYPTED : 0;
@@ -134,62 +133,65 @@
final String alias = mSpec.getKeystoreAlias();
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
-
byte[][] args = getArgsForKeyType(mKeyType, mSpec.getAlgorithmParameterSpec());
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
- if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, mKeyType, mKeySize,
- flags, args)) {
- throw new IllegalStateException("could not generate key in keystore");
- }
-
- Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
-
- final PrivateKey privKey;
- final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
+ boolean success = false;
try {
- privKey = engine.getPrivateKeyById(privateKeyAlias);
- } catch (InvalidKeyException e) {
- throw new RuntimeException("Can't get key", e);
- }
-
- final byte[] pubKeyBytes = mKeyStore.getPubkey(privateKeyAlias);
-
- final PublicKey pubKey;
- try {
- final KeyFactory keyFact = KeyFactory.getInstance(mKeyAlgorithm);
- pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
- } catch (NoSuchAlgorithmException e) {
- throw new IllegalStateException("Can't instantiate key generator", e);
- } catch (InvalidKeySpecException e) {
- throw new IllegalStateException("keystore returned invalid key encoding", e);
- }
-
- final X509Certificate cert;
- try {
- cert = generateCertificate(privKey, pubKey);
- } catch (Exception e) {
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
- throw new IllegalStateException("Can't generate certificate", e);
- }
+ if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, mKeyType, mKeySize,
+ flags, args)) {
+ throw new IllegalStateException("could not generate key in keystore");
+ }
- byte[] certBytes;
- try {
- certBytes = cert.getEncoded();
- } catch (CertificateEncodingException e) {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
- throw new IllegalStateException("Can't get encoding of certificate", e);
- }
+ final PrivateKey privKey;
+ final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
+ try {
+ privKey = engine.getPrivateKeyById(privateKeyAlias);
+ } catch (InvalidKeyException e) {
+ throw new RuntimeException("Can't get key", e);
+ }
- if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes, KeyStore.UID_SELF,
- flags)) {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
- throw new IllegalStateException("Can't store certificate in AndroidKeyStore");
- }
+ final byte[] pubKeyBytes = mKeyStore.getPubkey(privateKeyAlias);
- return new KeyPair(pubKey, privKey);
+ final PublicKey pubKey;
+ try {
+ final KeyFactory keyFact = KeyFactory.getInstance(mKeyAlgorithm);
+ pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException("Can't instantiate key generator", e);
+ } catch (InvalidKeySpecException e) {
+ throw new IllegalStateException("keystore returned invalid key encoding", e);
+ }
+
+ final X509Certificate cert;
+ try {
+ cert = generateCertificate(privKey, pubKey);
+ } catch (Exception e) {
+ throw new IllegalStateException("Can't generate certificate", e);
+ }
+
+ byte[] certBytes;
+ try {
+ certBytes = cert.getEncoded();
+ } catch (CertificateEncodingException e) {
+ throw new IllegalStateException("Can't get encoding of certificate", e);
+ }
+
+ if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes, KeyStore.UID_SELF,
+ flags)) {
+ throw new IllegalStateException("Can't store certificate in AndroidKeyStore");
+ }
+
+ KeyPair result = new KeyPair(pubKey, privKey);
+ success = true;
+ return result;
+ } finally {
+ if (!success) {
+ Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+ }
+ }
}
@SuppressWarnings("deprecation")
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 649a515..de4213e 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -20,6 +20,7 @@
import java.security.Provider;
import java.security.Security;
+import java.security.Signature;
import javax.crypto.Cipher;
import javax.crypto.Mac;
@@ -118,12 +119,15 @@
throw new NullPointerException();
}
Object spi;
- if (cryptoPrimitive instanceof Mac) {
+ if (cryptoPrimitive instanceof Signature) {
+ spi = ((Signature) cryptoPrimitive).getCurrentSpi();
+ } else if (cryptoPrimitive instanceof Mac) {
spi = ((Mac) cryptoPrimitive).getCurrentSpi();
} else if (cryptoPrimitive instanceof Cipher) {
spi = ((Cipher) cryptoPrimitive).getCurrentSpi();
} else {
- throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive);
+ throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive
+ + ". Supported: Signature, Mac, Cipher");
}
if (spi == null) {
throw new IllegalStateException("Crypto primitive not initialized");
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
index 36bc997b..08a173e 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
@@ -30,7 +30,7 @@
public AndroidKeyStoreRSAPublicKey(String alias, byte[] x509EncodedForm, BigInteger modulus,
BigInteger publicExponent) {
- super(alias, "RSA", x509EncodedForm);
+ super(alias, KeyProperties.KEY_ALGORITHM_RSA, x509EncodedForm);
mModulus = modulus;
mPublicExponent = publicExponent;
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java
new file mode 100644
index 0000000..898336d
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import android.annotation.NonNull;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+
+import java.security.InvalidKeyException;
+import java.security.SignatureSpi;
+
+/**
+ * Base class for {@link SignatureSpi} providing Android KeyStore backed RSA signatures.
+ *
+ * @hide
+ */
+abstract class AndroidKeyStoreRSASignatureSpi extends AndroidKeyStoreSignatureSpiBase {
+
+ abstract static class PKCS1Padding extends AndroidKeyStoreRSASignatureSpi {
+ PKCS1Padding(int keymasterDigest) {
+ super(keymasterDigest, KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN);
+ }
+
+ @Override
+ protected final int getAdditionalEntropyAmountForBegin() {
+ // No entropy required for this deterministic signature scheme.
+ return 0;
+ }
+ }
+
+ public static final class NONEWithPKCS1Padding extends PKCS1Padding {
+ public NONEWithPKCS1Padding() {
+ super(KeymasterDefs.KM_DIGEST_NONE);
+ }
+ }
+
+ public static final class MD5WithPKCS1Padding extends PKCS1Padding {
+ public MD5WithPKCS1Padding() {
+ super(KeymasterDefs.KM_DIGEST_MD5);
+ }
+ }
+
+ public static final class SHA1WithPKCS1Padding extends PKCS1Padding {
+ public SHA1WithPKCS1Padding() {
+ super(KeymasterDefs.KM_DIGEST_SHA1);
+ }
+ }
+
+ public static final class SHA224WithPKCS1Padding extends PKCS1Padding {
+ public SHA224WithPKCS1Padding() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_224);
+ }
+ }
+
+ public static final class SHA256WithPKCS1Padding extends PKCS1Padding {
+ public SHA256WithPKCS1Padding() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_256);
+ }
+ }
+
+ public static final class SHA384WithPKCS1Padding extends PKCS1Padding {
+ public SHA384WithPKCS1Padding() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_384);
+ }
+ }
+
+ public static final class SHA512WithPKCS1Padding extends PKCS1Padding {
+ public SHA512WithPKCS1Padding() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_512);
+ }
+ }
+
+ abstract static class PSSPadding extends AndroidKeyStoreRSASignatureSpi {
+ private static final int SALT_LENGTH_BYTES = 20;
+
+ PSSPadding(int keymasterDigest) {
+ super(keymasterDigest, KeymasterDefs.KM_PAD_RSA_PSS);
+ }
+
+ @Override
+ protected final int getAdditionalEntropyAmountForBegin() {
+ return (isSigning()) ? SALT_LENGTH_BYTES : 0;
+ }
+ }
+
+ public static final class SHA1WithPSSPadding extends PSSPadding {
+ public SHA1WithPSSPadding() {
+ super(KeymasterDefs.KM_DIGEST_SHA1);
+ }
+ }
+
+ public static final class SHA224WithPSSPadding extends PSSPadding {
+ public SHA224WithPSSPadding() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_224);
+ }
+ }
+
+ public static final class SHA256WithPSSPadding extends PSSPadding {
+ public SHA256WithPSSPadding() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_256);
+ }
+ }
+
+ public static final class SHA384WithPSSPadding extends PSSPadding {
+ public SHA384WithPSSPadding() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_384);
+ }
+ }
+
+ public static final class SHA512WithPSSPadding extends PSSPadding {
+ public SHA512WithPSSPadding() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_512);
+ }
+ }
+
+ private final int mKeymasterDigest;
+ private final int mKeymasterPadding;
+
+ AndroidKeyStoreRSASignatureSpi(int keymasterDigest, int keymasterPadding) {
+ mKeymasterDigest = keymasterDigest;
+ mKeymasterPadding = keymasterPadding;
+ }
+
+ @Override
+ protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
+ if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) {
+ throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
+ + ". Only" + KeyProperties.KEY_ALGORITHM_RSA + " supported");
+ }
+ super.initKey(key);
+ }
+
+ @Override
+ protected final void resetAll() {
+ super.resetAll();
+ }
+
+ @Override
+ protected final void resetWhilePreservingInitState() {
+ super.resetWhilePreservingInitState();
+ }
+
+ @Override
+ protected final void addAlgorithmSpecificParametersToBegin(
+ @NonNull KeymasterArguments keymasterArgs) {
+ keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+ keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
+ keymasterArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+ }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
new file mode 100644
index 0000000..4c4062f
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.os.IBinder;
+import android.security.KeyStore;
+import android.security.KeyStoreException;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keymaster.OperationResult;
+
+import com.android.org.conscrypt.util.EmptyArray;
+
+import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.PrivateKey;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.SignatureSpi;
+
+/**
+ * Base class for {@link SignatureSpi} implementations of Android KeyStore backed ciphers.
+ *
+ * @hide
+ */
+abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
+ implements KeyStoreCryptoOperation {
+ private final KeyStore mKeyStore;
+
+ // Fields below are populated by SignatureSpi.engineInitSign/engineInitVerify and KeyStore.begin
+ // and should be preserved after SignatureSpi.engineSign/engineVerify finishes.
+ private boolean mSigning;
+ private AndroidKeyStoreKey mKey;
+
+ /**
+ * Token referencing this operation inside keystore service. It is initialized by
+ * {@code engineInitSign}/{@code engineInitVerify} and is invalidated when
+ * {@code engineSign}/{@code engineVerify} succeeds and on some error conditions in between.
+ */
+ private IBinder mOperationToken;
+ private long mOperationHandle;
+ private KeyStoreCryptoOperationChunkedStreamer mMessageStreamer;
+
+ /**
+ * Encountered exception which could not be immediately thrown because it was encountered inside
+ * a method that does not throw checked exception. This exception will be thrown from
+ * {@code engineSign} or {@code engineVerify}. Once such an exception is encountered,
+ * {@code engineUpdate} starts ignoring input data.
+ */
+ private Exception mCachedException;
+
+ AndroidKeyStoreSignatureSpiBase() {
+ mKeyStore = KeyStore.getInstance();
+ }
+
+ @Override
+ protected final void engineInitSign(PrivateKey key) throws InvalidKeyException {
+ engineInitSign(key, null);
+ }
+
+ @Override
+ protected final void engineInitSign(PrivateKey privateKey, SecureRandom random)
+ throws InvalidKeyException {
+ resetAll();
+
+ boolean success = false;
+ try {
+ if (privateKey == null) {
+ throw new InvalidKeyException("Unsupported key: null");
+ }
+ AndroidKeyStoreKey keystoreKey;
+ if (privateKey instanceof AndroidKeyStorePrivateKey) {
+ keystoreKey = (AndroidKeyStoreKey) privateKey;
+ } else {
+ throw new InvalidKeyException("Unsupported private key type: " + privateKey);
+ }
+ mSigning = true;
+ initKey(keystoreKey);
+ appRandom = random;
+ ensureKeystoreOperationInitialized();
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
+ }
+
+ @Override
+ protected final void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+ resetAll();
+
+ boolean success = false;
+ try {
+ if (publicKey == null) {
+ throw new InvalidKeyException("Unsupported key: null");
+ }
+ AndroidKeyStoreKey keystoreKey;
+ if (publicKey instanceof AndroidKeyStorePublicKey) {
+ keystoreKey = (AndroidKeyStorePublicKey) publicKey;
+ } else {
+ throw new InvalidKeyException("Unsupported public key type: " + publicKey);
+ }
+ mSigning = false;
+ initKey(keystoreKey);
+ appRandom = null;
+ ensureKeystoreOperationInitialized();
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
+ }
+
+ /**
+ * Configures this signature instance to use the provided key.
+ *
+ * @throws InvalidKeyException if the {@code key} is not suitable.
+ */
+ @CallSuper
+ protected void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
+ mKey = key;
+ }
+
+ /**
+ * Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new
+ * cipher instance.
+ *
+ * <p>Subclasses storing additional state should override this method, reset the additional
+ * state, and then chain to superclass.
+ */
+ @CallSuper
+ protected void resetAll() {
+ IBinder operationToken = mOperationToken;
+ if (operationToken != null) {
+ mOperationToken = null;
+ mKeyStore.abort(operationToken);
+ }
+ mSigning = false;
+ mKey = null;
+ appRandom = null;
+ mOperationToken = null;
+ mOperationHandle = 0;
+ mMessageStreamer = null;
+ mCachedException = null;
+ }
+
+ /**
+ * Resets this cipher while preserving the initialized state. This must be equivalent to
+ * rolling back the cipher's state to just after the most recent {@code engineInit} completed
+ * successfully.
+ *
+ * <p>Subclasses storing additional post-init state should override this method, reset the
+ * additional state, and then chain to superclass.
+ */
+ @CallSuper
+ protected void resetWhilePreservingInitState() {
+ IBinder operationToken = mOperationToken;
+ if (operationToken != null) {
+ mOperationToken = null;
+ mKeyStore.abort(operationToken);
+ }
+ mOperationHandle = 0;
+ mMessageStreamer = null;
+ mCachedException = null;
+ }
+
+ private void ensureKeystoreOperationInitialized() throws InvalidKeyException {
+ if (mMessageStreamer != null) {
+ return;
+ }
+ if (mCachedException != null) {
+ return;
+ }
+ if (mKey == null) {
+ throw new IllegalStateException("Not initialized");
+ }
+
+ KeymasterArguments keymasterInputArgs = new KeymasterArguments();
+ addAlgorithmSpecificParametersToBegin(keymasterInputArgs);
+ byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+ appRandom, getAdditionalEntropyAmountForBegin());
+
+ OperationResult opResult = mKeyStore.begin(
+ mKey.getAlias(),
+ mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY,
+ true, // permit aborting this operation if keystore runs out of resources
+ keymasterInputArgs,
+ additionalEntropy);
+ if (opResult == null) {
+ throw new KeyStoreConnectException();
+ }
+
+ // Store operation token and handle regardless of the error code returned by KeyStore to
+ // ensure that the operation gets aborted immediately if the code below throws an exception.
+ mOperationToken = opResult.token;
+ mOperationHandle = opResult.operationHandle;
+
+ // If necessary, throw an exception due to KeyStore operation having failed.
+ InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(
+ mKeyStore, mKey, opResult.resultCode);
+ if (e != null) {
+ throw e;
+ }
+
+ if (mOperationToken == null) {
+ throw new ProviderException("Keystore returned null operation token");
+ }
+ if (mOperationHandle == 0) {
+ throw new ProviderException("Keystore returned invalid operation handle");
+ }
+
+ mMessageStreamer = new KeyStoreCryptoOperationChunkedStreamer(
+ new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
+ mKeyStore, opResult.token));
+ }
+
+ @Override
+ public final long getOperationHandle() {
+ return mOperationHandle;
+ }
+
+ @Override
+ protected final void engineUpdate(byte[] b, int off, int len) throws SignatureException {
+ if (mCachedException != null) {
+ throw new SignatureException(mCachedException);
+ }
+
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidKeyException e) {
+ throw new SignatureException(e);
+ }
+
+ if (len == 0) {
+ return;
+ }
+
+ byte[] output;
+ try {
+ output = mMessageStreamer.update(b, off, len);
+ } catch (KeyStoreException e) {
+ throw new SignatureException(e);
+ }
+
+ if (output.length != 0) {
+ throw new ProviderException(
+ "Update operation unexpectedly produced output: " + output.length + " bytes");
+ }
+ }
+
+ @Override
+ protected final void engineUpdate(byte b) throws SignatureException {
+ engineUpdate(new byte[] {b}, 0, 1);
+ }
+
+ @Override
+ protected final void engineUpdate(ByteBuffer input) {
+ byte[] b;
+ int off;
+ int len = input.remaining();
+ if (input.hasArray()) {
+ b = input.array();
+ off = input.arrayOffset() + input.position();
+ input.position(input.limit());
+ } else {
+ b = new byte[len];
+ off = 0;
+ input.get(b);
+ }
+
+ try {
+ engineUpdate(b, off, len);
+ } catch (SignatureException e) {
+ mCachedException = e;
+ }
+ }
+
+ @Override
+ protected final int engineSign(byte[] out, int outOffset, int outLen)
+ throws SignatureException {
+ return super.engineSign(out, outOffset, outLen);
+ }
+
+ @Override
+ protected final byte[] engineSign() throws SignatureException {
+ if (mCachedException != null) {
+ throw new SignatureException(mCachedException);
+ }
+
+ byte[] signature;
+ try {
+ ensureKeystoreOperationInitialized();
+ signature = mMessageStreamer.doFinal(EmptyArray.BYTE, 0, 0);
+ } catch (InvalidKeyException | KeyStoreException e) {
+ throw new SignatureException(e);
+ }
+
+ resetWhilePreservingInitState();
+ return signature;
+ }
+
+ @Override
+ protected final boolean engineVerify(byte[] signature) throws SignatureException {
+ if (mCachedException != null) {
+ throw new SignatureException(mCachedException);
+ }
+
+ boolean result;
+ try {
+ ensureKeystoreOperationInitialized();
+ mMessageStreamer.flush();
+ OperationResult opResult = mKeyStore.finish(mOperationToken, null, signature);
+ if (opResult == null) {
+ throw new KeyStoreConnectException();
+ }
+ switch (opResult.resultCode) {
+ case KeyStore.NO_ERROR:
+ result = true;
+ break;
+ case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
+ result = false;
+ break;
+ default:
+ throw new SignatureException(
+ KeyStore.getKeyStoreException(opResult.resultCode));
+ }
+ } catch (InvalidKeyException | KeyStoreException e) {
+ throw new SignatureException(e);
+ }
+
+ resetWhilePreservingInitState();
+ return result;
+ }
+
+ @Override
+ protected final boolean engineVerify(byte[] sigBytes, int offset, int len)
+ throws SignatureException {
+ return engineVerify(ArrayUtils.subarray(sigBytes, offset, len));
+ }
+
+ @Deprecated
+ @Override
+ protected final Object engineGetParameter(String param) throws InvalidParameterException {
+ throw new InvalidParameterException();
+ }
+
+ @Deprecated
+ @Override
+ protected final void engineSetParameter(String param, Object value)
+ throws InvalidParameterException {
+ throw new InvalidParameterException();
+ }
+
+ protected final KeyStore getKeyStore() {
+ return mKeyStore;
+ }
+
+ /**
+ * Returns {@code true} if this signature is initialized for signing, {@code false} if this
+ * signature is initialized for verification.
+ */
+ protected final boolean isSigning() {
+ return mSigning;
+ }
+
+ // The methods below need to be implemented by subclasses.
+
+ /**
+ * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
+ * {@code begin} operation.
+ *
+ * <p>For signature verification, this should be {@code 0} because verification should not be
+ * consuming any entropy. For signature generation, this value should match (or exceed) the
+ * amount of Shannon entropy of the produced signature assuming the key and the message are
+ * known. For example, for ECDSA signature this should be the size of {@code R}, whereas for the
+ * RSA signature with PKCS#1 padding this should be {@code 0}.
+ */
+ protected abstract int getAdditionalEntropyAmountForBegin();
+
+ /**
+ * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
+ *
+ * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific
+ * parameters.
+ */
+ protected abstract void addAlgorithmSpecificParametersToBegin(
+ @NonNull KeymasterArguments keymasterArgs);
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 701bd67..05ddef6 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -674,12 +674,13 @@
@Override
public void engineDeleteEntry(String alias) throws KeyStoreException {
- if (!isKeyEntry(alias) && !isCertificateEntry(alias)) {
+ if (!engineContainsAlias(alias)) {
return;
}
+ // At least one entry corresponding to this alias exists in keystore
if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
- throw new KeyStoreException("No such entry " + alias);
+ throw new KeyStoreException("Failed to delete entry: " + alias);
}
}
@@ -849,9 +850,7 @@
throw new KeyStoreException("entry == null");
}
- if (engineContainsAlias(alias)) {
- engineDeleteEntry(alias);
- }
+ Credentials.deleteAllTypesForAlias(mKeyStore, alias);
if (entry instanceof KeyStore.TrustedCertificateEntry) {
KeyStore.TrustedCertificateEntry trE = (KeyStore.TrustedCertificateEntry) entry;
diff --git a/media/java/android/media/ClosedCaptionRenderer.java b/media/java/android/media/ClosedCaptionRenderer.java
index e3680e9..8403c1c 100644
--- a/media/java/android/media/ClosedCaptionRenderer.java
+++ b/media/java/android/media/ClosedCaptionRenderer.java
@@ -1044,42 +1044,26 @@
}
/**
+ * Mutable version of BackgroundSpan to facilitate text rendering with edge
+ * styles.
+ *
* @hide
- *
- * MutableBackgroundColorSpan
- *
- * This is a mutable version of BackgroundSpan to facilitate text
- * rendering with edge styles.
- *
*/
-class MutableBackgroundColorSpan extends CharacterStyle
- implements UpdateAppearance, ParcelableSpan {
+class MutableBackgroundColorSpan extends CharacterStyle implements UpdateAppearance {
private int mColor;
public MutableBackgroundColorSpan(int color) {
mColor = color;
}
- public MutableBackgroundColorSpan(Parcel src) {
- mColor = src.readInt();
- }
+
public void setBackgroundColor(int color) {
mColor = color;
}
+
public int getBackgroundColor() {
return mColor;
}
- @Override
- public int getSpanTypeId() {
- return TextUtils.BACKGROUND_COLOR_SPAN;
- }
- @Override
- public int describeContents() {
- return 0;
- }
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mColor);
- }
+
@Override
public void updateDrawState(TextPaint ds) {
ds.bgColor = mColor;
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 8441541..166ff38 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -21,6 +21,7 @@
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources.NotFoundException;
import android.database.Cursor;
+import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;
import android.os.Binder;
import android.os.RemoteException;
@@ -29,6 +30,7 @@
import android.util.Log;
import java.io.IOException;
+import java.util.ArrayList;
/**
* Ringtone provides a quick method for playing a ringtone, notification, or
@@ -49,6 +51,9 @@
MediaStore.Audio.Media.TITLE
};
+ // keep references on active Ringtones until stopped or completion listener called.
+ private static final ArrayList<Ringtone> sActiveRingtones = new ArrayList<Ringtone>();
+
private final Context mContext;
private final AudioManager mAudioManager;
@@ -62,6 +67,7 @@
private final Binder mRemoteToken;
private MediaPlayer mLocalPlayer;
+ private final MyOnCompletionListener mCompletionListener = new MyOnCompletionListener();
private Uri mUri;
private String mTitle;
@@ -247,7 +253,7 @@
// (typically because ringer mode is silent).
if (mAudioManager.getStreamVolume(
AudioAttributes.toLegacyStreamType(mAudioAttributes)) != 0) {
- mLocalPlayer.start();
+ startLocalPlayer();
}
} else if (mAllowRemote && (mRemotePlayer != null)) {
final Uri canonicalUri = mUri.getCanonicalUri();
@@ -285,9 +291,23 @@
mLocalPlayer.reset();
mLocalPlayer.release();
mLocalPlayer = null;
+ synchronized (sActiveRingtones) {
+ sActiveRingtones.remove(this);
+ }
}
}
+ private void startLocalPlayer() {
+ if (mLocalPlayer == null) {
+ return;
+ }
+ synchronized (sActiveRingtones) {
+ sActiveRingtones.add(this);
+ }
+ mLocalPlayer.setOnCompletionListener(mCompletionListener);
+ mLocalPlayer.start();
+ }
+
/**
* Whether this ringtone is currently playing.
*
@@ -330,7 +350,7 @@
}
mLocalPlayer.setAudioAttributes(mAudioAttributes);
mLocalPlayer.prepare();
- mLocalPlayer.start();
+ startLocalPlayer();
afd.close();
return true;
} else {
@@ -352,4 +372,20 @@
void setTitle(String title) {
mTitle = title;
}
+
+ @Override
+ protected void finalize() {
+ if (mLocalPlayer != null) {
+ mLocalPlayer.release();
+ }
+ }
+
+ class MyOnCompletionListener implements MediaPlayer.OnCompletionListener {
+ public void onCompletion(MediaPlayer mp)
+ {
+ synchronized (sActiveRingtones) {
+ sActiveRingtones.remove(Ringtone.this);
+ }
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index d3e7104..9a2f71c 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -348,8 +348,12 @@
Log.e(TAG, "ACTION_PAIRING_CANCEL with no EXTRA_DEVICE");
return;
}
- int errorMsg = R.string.bluetooth_pairing_error_message;
CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice == null) {
+ Log.e(TAG, "ACTION_PAIRING_CANCEL with no cached device");
+ return;
+ }
+ int errorMsg = R.string.bluetooth_pairing_error_message;
Utils.showError(context, cachedDevice.getName(), errorMsg);
}
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index fd0ba73..640fb29 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -25,6 +25,7 @@
<!-- Standard permissions granted to the shell. -->
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
@@ -97,6 +98,10 @@
<uses-permission android:name="android.permission.CHANGE_APP_IDLE_STATE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS" />
+ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+ <uses-permission android:name="android.permission.REGISTER_CALL_PROVIDER" />
+ <uses-permission android:name="android.permission.REGISTER_CONNECTION_MANAGER" />
+ <uses-permission android:name="android.permission.REGISTER_SIM_SUBSCRIPTION" />
<application android:label="@string/app_label">
<provider
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index 53047a3..477d9d7 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -21,6 +21,7 @@
android:layout_gravity="top|center_horizontal">
<com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/application_icon"
+ android:contentDescription="@string/recents_app_info_button_label"
android:layout_width="@dimen/recents_task_view_application_icon_size"
android:layout_height="@dimen/recents_task_view_application_icon_size"
android:layout_marginStart="8dp"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 18a19cb..9f38185 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -336,7 +336,7 @@
<dimen name="keyguard_affordance_min_background_radius">30dp</dimen>
<!-- The size of the touch targets on the keyguard for the affordances. -->
- <dimen name="keyguard_affordance_touch_target_size">96dp</dimen>
+ <dimen name="keyguard_affordance_touch_target_size">120dp</dimen>
<!-- The grow amount for the camera and phone circles when hinting -->
<dimen name="hint_grow_amount_sideways">60dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 0369ab5..5d74604 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -74,7 +74,11 @@
state.visible = mFlashlightController.isAvailable();
state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
if (arg instanceof UserBoolean) {
- state.value = ((UserBoolean) arg).value;
+ boolean value = ((UserBoolean) arg).value;
+ if (value == state.value) {
+ return;
+ }
+ state.value = value;
}
final AnimationIcon icon = state.value ? mEnable : mDisable;
icon.setAllowAnimation(arg instanceof UserBoolean && ((UserBoolean) arg).userInitiated);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index c27e3a8..b0247cb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.graphics.*;
import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityManager;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.view.animation.AccelerateInterpolator;
@@ -676,7 +677,11 @@
mThumbnailView.rebindToTask(mTask);
mHeaderView.rebindToTask(mTask);
// Rebind any listeners
- mHeaderView.mApplicationIcon.setOnClickListener(this);
+ AccessibilityManager am = (AccessibilityManager) getContext().
+ getSystemService(Context.ACCESSIBILITY_SERVICE);
+ if (Constants.DebugFlags.App.EnableTaskFiltering || (am != null && am.isEnabled())) {
+ mHeaderView.mApplicationIcon.setOnClickListener(this);
+ }
mHeaderView.mDismissButton.setOnClickListener(this);
if (mConfig.multiStackEnabled) {
mHeaderView.mMoveTaskButton.setOnClickListener(this);
@@ -722,9 +727,19 @@
postDelayed(new Runnable() {
@Override
public void run() {
- if (Constants.DebugFlags.App.EnableTaskFiltering && v == mHeaderView.mApplicationIcon) {
- if (mCb != null) {
- mCb.onTaskViewAppIconClicked(tv);
+ if (v == mHeaderView.mApplicationIcon) {
+ if (Constants.DebugFlags.App.EnableTaskFiltering) {
+ if (mCb != null) {
+ mCb.onTaskViewAppIconClicked(tv);
+ }
+ } else {
+ AccessibilityManager am = (AccessibilityManager) getContext().
+ getSystemService(Context.ACCESSIBILITY_SERVICE);
+ if (am != null && am.isEnabled()) {
+ if (mCb != null) {
+ mCb.onTaskViewAppInfoClicked(tv);
+ }
+ }
}
} else if (v == mHeaderView.mDismissButton) {
dismissTask();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 09e9839..1a22cae 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -194,7 +194,6 @@
} else if (t.applicationIcon != null) {
mApplicationIcon.setImageDrawable(t.applicationIcon);
}
- mApplicationIcon.setContentDescription(t.contentDescription);
if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
mActivityDescription.setText(t.activityLabel);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 9ccff72..374d970 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -23,11 +23,14 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.view.DisplayListCanvas;
+import android.view.RenderNodeAnimator;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.animation.AnimationUtils;
@@ -35,6 +38,7 @@
import android.widget.ImageView;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.KeyguardAffordanceHelper;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
/**
* An ImageView which does not have overlapping renderings commands and therefore does not need a
@@ -77,6 +81,14 @@
private float mMaxCircleSize;
private Animator mPreviewClipper;
private float mRestingAlpha = KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT;
+ private boolean mSupportHardware;
+ private boolean mFinishing;
+
+ private CanvasProperty<Float> mHwCircleRadius;
+ private CanvasProperty<Float> mHwCenterX;
+ private CanvasProperty<Float> mHwCenterY;
+ private CanvasProperty<Paint> mHwCirclePaint;
+
private AnimatorListenerAdapter mClipEndListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -155,6 +167,7 @@
@Override
protected void onDraw(Canvas canvas) {
+ mSupportHardware = canvas.isHardwareAccelerated();
drawBackgroundCircle(canvas);
drawArrow(canvas);
canvas.save();
@@ -196,8 +209,14 @@
private void drawBackgroundCircle(Canvas canvas) {
if (mCircleRadius > 0) {
- updateCircleColor();
- canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mCirclePaint);
+ if (mFinishing && mSupportHardware) {
+ DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
+ displayListCanvas.drawCircle(mHwCenterX, mHwCenterY, mHwCircleRadius,
+ mHwCirclePaint);
+ } else {
+ updateCircleColor();
+ canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mCirclePaint);
+ }
}
}
@@ -218,15 +237,23 @@
public void finishAnimation(float velocity, final Runnable mAnimationEndRunnable) {
cancelAnimator(mCircleAnimator);
cancelAnimator(mPreviewClipper);
+ mFinishing = true;
mCircleStartRadius = mCircleRadius;
float maxCircleSize = getMaxCircleSize();
- ValueAnimator animatorToRadius = getAnimatorToRadius(maxCircleSize);
+ Animator animatorToRadius;
+ if (mSupportHardware) {
+ initHwProperties();
+ animatorToRadius = getRtAnimatorToRadius(maxCircleSize);
+ } else {
+ animatorToRadius = getAnimatorToRadius(maxCircleSize);
+ }
mFlingAnimationUtils.applyDismissing(animatorToRadius, mCircleRadius, maxCircleSize,
velocity, maxCircleSize);
animatorToRadius.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mAnimationEndRunnable.run();
+ mFinishing = false;
}
});
animatorToRadius.start();
@@ -240,9 +267,34 @@
velocity, maxCircleSize);
mPreviewClipper.addListener(mClipEndListener);
mPreviewClipper.start();
+ if (mSupportHardware) {
+ startRtCircleFadeOut(animatorToRadius.getDuration());
+ }
}
}
+ private void startRtCircleFadeOut(long duration) {
+ RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
+ RenderNodeAnimator.PAINT_ALPHA, 0);
+ animator.setDuration(duration);
+ animator.setInterpolator(PhoneStatusBar.ALPHA_OUT);
+ animator.setTarget(this);
+ animator.start();
+ }
+
+ private Animator getRtAnimatorToRadius(float circleRadius) {
+ RenderNodeAnimator animator = new RenderNodeAnimator(mHwCircleRadius, circleRadius);
+ animator.setTarget(this);
+ return animator;
+ }
+
+ private void initHwProperties() {
+ mHwCenterX = CanvasProperty.createFloat(mCenterX);
+ mHwCenterY = CanvasProperty.createFloat(mCenterY);
+ mHwCirclePaint = CanvasProperty.createPaint(mCirclePaint);
+ mHwCircleRadius = CanvasProperty.createFloat(mCircleRadius);
+ }
+
private float getMaxCircleSize() {
getLocationInWindow(mTempPoint);
float rootWidth = getRootView().getWidth();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 2fe98bb..4bc317a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -196,6 +196,7 @@
private boolean mCollapsedOnDown;
private int mPositionMinSideMargin;
private int mLastOrientation = -1;
+ private boolean mClosingWithAlphaFadeOut;
private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
@Override
@@ -527,6 +528,7 @@
protected void flingToHeight(float vel, boolean expand, float target,
float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
mHeadsUpTouchHelper.notifyFling(!expand);
+ setClosingWithAlphaFadeout(!expand && getFadeoutAlpha() == 1.0f);
super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
}
@@ -638,10 +640,9 @@
@Override
protected boolean isInContentBounds(float x, float y) {
- float yTransformed = y - mNotificationStackScroller.getY();
float stackScrollerX = mNotificationStackScroller.getX();
- return mNotificationStackScroller.isInContentBounds(yTransformed) && stackScrollerX < x
- && x < stackScrollerX + mNotificationStackScroller.getWidth();
+ return !mNotificationStackScroller.isBelowLastNotification(x - stackScrollerX, y)
+ && stackScrollerX < x && x < stackScrollerX + mNotificationStackScroller.getWidth();
}
private void initDownStates(MotionEvent event) {
@@ -1074,8 +1075,12 @@
};
private void animateHeaderSlidingIn() {
- mHeaderAnimating = true;
- getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
+ // If the QS is already expanded we don't need to slide in the header as it's already
+ // visible.
+ if (!mQsExpanded) {
+ mHeaderAnimating = true;
+ getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
+ }
}
private void animateHeaderSlidingOut() {
@@ -1585,26 +1590,22 @@
}
}
private void updateNotificationTranslucency() {
- float alpha;
- if (mExpandingFromHeadsUp || mHeadsUpManager.hasPinnedHeadsUp()) {
- alpha = 1f;
- } else {
- alpha = (getNotificationsTopY() + mNotificationStackScroller.getItemHeight())
- / (mQsMinExpansionHeight + mNotificationStackScroller.getBottomStackPeekSize()
- - mNotificationStackScroller.getCollapseSecondCardPadding());
- alpha = Math.max(0, Math.min(alpha, 1));
- alpha = (float) Math.pow(alpha, 0.75);
- }
-
- if (alpha != 1f && mNotificationStackScroller.getLayerType() != LAYER_TYPE_HARDWARE) {
- mNotificationStackScroller.setLayerType(LAYER_TYPE_HARDWARE, null);
- } else if (alpha == 1f
- && mNotificationStackScroller.getLayerType() == LAYER_TYPE_HARDWARE) {
- mNotificationStackScroller.setLayerType(LAYER_TYPE_NONE, null);
+ float alpha = 1f;
+ if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp && !mHeadsUpManager.hasPinnedHeadsUp()) {
+ alpha = getFadeoutAlpha();
}
mNotificationStackScroller.setAlpha(alpha);
}
+ private float getFadeoutAlpha() {
+ float alpha = (getNotificationsTopY() + mNotificationStackScroller.getItemHeight())
+ / (mQsMinExpansionHeight + mNotificationStackScroller.getBottomStackPeekSize()
+ - mNotificationStackScroller.getCollapseSecondCardPadding());
+ alpha = Math.max(0, Math.min(alpha, 1));
+ alpha = (float) Math.pow(alpha, 0.75);
+ return alpha;
+ }
+
@Override
protected float getOverExpansionAmount() {
return mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */);
@@ -2261,6 +2262,12 @@
protected void onClosingFinished() {
super.onClosingFinished();
resetVerticalPanelPosition();
+ setClosingWithAlphaFadeout(false);
+ }
+
+ private void setClosingWithAlphaFadeout(boolean closing) {
+ mClosingWithAlphaFadeOut = closing;
+ mNotificationStackScroller.forceNoOverlappingRendering(closing);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 6d35ff0..9d4997c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -777,6 +777,15 @@
public void setExpandedFraction(float frac) {
setExpandedHeight(getMaxPanelHeight() * frac);
+ if (PhoneStatusBar.DEBUG_EMPTY_KEYGUARD
+ && mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+ if (frac == 0.0f) {
+ Log.i(PhoneStatusBar.TAG, "Panel collapsed! Stacktrace: "
+ + Log.getStackTraceString(new Throwable()));
+ } else if (frac == 1.0f) {
+ mStatusBar.endWindowManagerLogging();
+ }
+ }
}
public float getExpandedHeight() {
@@ -808,6 +817,11 @@
}
public void collapse(boolean delayed, float speedUpFactor) {
+ if (PhoneStatusBar.DEBUG_EMPTY_KEYGUARD
+ && mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+ Log.i(PhoneStatusBar.TAG, "Panel collapsed! Stacktrace: "
+ + Log.getStackTraceString(new Throwable()));
+ }
if (DEBUG) logf("collapse: " + this);
if (mPeekPending || mPeekAnimator != null) {
mCollapseAfterPeek = true;
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 def95aa..7c7bec9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -185,6 +185,7 @@
HeadsUpManager.OnHeadsUpChangedListener {
static final String TAG = "PhoneStatusBar";
public static final boolean DEBUG = BaseStatusBar.DEBUG;
+ public static final boolean DEBUG_EMPTY_KEYGUARD = true;
public static final boolean SPEW = false;
public static final boolean DUMPTRUCK = true; // extra dumpsys info
public static final boolean DEBUG_GESTURES = false;
@@ -2003,6 +2004,10 @@
mStatusBarWindowManager.setPanelExpanded(isExpanded);
}
+ public void endWindowManagerLogging() {
+ mStatusBarWindowManager.setLogState(false);
+ }
+
/**
* All changes to the status bar and notifications funnel through here and are batched.
*/
@@ -3547,6 +3552,9 @@
// Make our window larger and the panel expanded.
makeExpandedVisible(true);
mNotificationPanel.instantExpand();
+ if (DEBUG_EMPTY_KEYGUARD) {
+ mStatusBarWindowManager.setLogState(true);
+ }
}
private void instantCollapseNotificationPanel() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index aa499ad..b7e675d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -20,12 +20,14 @@
import android.content.res.Resources;
import android.util.AttributeSet;
import android.util.EventLog;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusBarState;
public class PhoneStatusBarView extends PanelBar {
private static final String TAG = "PhoneStatusBarView";
@@ -108,7 +110,11 @@
@Override
public void onAllPanelsCollapsed() {
super.onAllPanelsCollapsed();
-
+ if (PhoneStatusBar.DEBUG_EMPTY_KEYGUARD
+ && mBar.getBarState() == StatusBarState.KEYGUARD) {
+ Log.i(PhoneStatusBar.TAG, "Panel collapsed! Stacktrace: "
+ + Log.getStackTraceString(new Throwable()));
+ }
// Close the status bar in the next frame so we can show the end of the animation.
postOnAnimation(new Runnable() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index de426430..58017d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -21,6 +21,7 @@
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.os.SystemProperties;
+import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -31,6 +32,8 @@
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.StatusBarState;
+import java.lang.reflect.Field;
+
/**
* Encapsulates all logic for the status bar window state management.
*/
@@ -45,6 +48,7 @@
private final boolean mKeyguardScreenRotation;
private final State mCurrentState = new State();
+ private boolean mLogState;
public StatusBarWindowManager(Context context) {
mContext = context;
@@ -129,9 +133,7 @@
}
private void applyHeight(State state) {
- boolean expanded = !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
- || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
- || state.headsUpShowing);
+ boolean expanded = isExpanded(state);
if (expanded) {
mLpChanged.height = ViewGroup.LayoutParams.MATCH_PARENT;
} else {
@@ -139,6 +141,12 @@
}
}
+ private boolean isExpanded(State state) {
+ return !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
+ || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
+ || state.headsUpShowing);
+ }
+
private void applyFitsSystemWindows(State state) {
mStatusBarView.setFitsSystemWindows(!state.isKeyguardShowingAndNotOccluded());
}
@@ -176,6 +184,9 @@
applyFitsSystemWindows(state);
applyModalFlag(state);
if (mLp.copyFrom(mLpChanged) != 0) {
+ if (PhoneStatusBar.DEBUG_EMPTY_KEYGUARD && mLogState) {
+ logCurrentState();
+ }
mWindowManager.updateViewLayout(mStatusBarView, mLp);
}
}
@@ -272,6 +283,21 @@
apply(mCurrentState);
}
+ public void setLogState(boolean logState) {
+ mLogState = logState;
+ if (logState) {
+ Log.w(PhoneStatusBar.TAG, "===== Started logging WM state changes =====");
+ logCurrentState();
+ } else {
+ Log.w(PhoneStatusBar.TAG, "===== Finished logging WM state changes =====");
+ }
+ }
+
+ private void logCurrentState() {
+ Log.i(PhoneStatusBar.TAG, mCurrentState.toString()
+ + "\n Expanded: " + isExpanded(mCurrentState));
+ }
+
private static class State {
boolean keyguardShowing;
boolean keyguardOccluded;
@@ -294,5 +320,31 @@
private boolean isKeyguardShowingAndNotOccluded() {
return keyguardShowing && !keyguardOccluded;
}
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ String newLine = "\n";
+ result.append("Window State {");
+ result.append(newLine);
+
+ Field[] fields = this.getClass().getDeclaredFields();
+
+ // Print field names paired with their values
+ for (Field field : fields) {
+ result.append(" ");
+ try {
+ result.append(field.getName());
+ result.append(": ");
+ //requires access to private field:
+ result.append(field.get(this));
+ } catch (IllegalAccessException ex) {
+ }
+ result.append(newLine);
+ }
+ result.append("}");
+
+ return result.toString();
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 3b91751..6a8f8ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -171,7 +171,6 @@
if (mNotificationPanel.isFullyExpanded()
&& mStackScrollLayout.getVisibility() == View.VISIBLE
&& mService.getBarState() == StatusBarState.KEYGUARD
- && !mService.isQsExpanded()
&& !mService.isBouncerShowing()) {
intercept = mDragDownHelper.onInterceptTouchEvent(ev);
// wake up on a touch down event, if dozing
@@ -195,7 +194,7 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
- if (mService.getBarState() == StatusBarState.KEYGUARD && !mService.isQsExpanded()) {
+ if (mService.getBarState() == StatusBarState.KEYGUARD) {
handled = mDragDownHelper.onTouchEvent(ev);
}
if (!handled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 4ae800f..d8f6bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -225,6 +225,7 @@
private HeadsUpManager mHeadsUpManager;
private boolean mTrackingHeadsUp;
private ScrimController mScrimController;
+ private boolean mForceNoOverlappingRendering;
public NotificationStackScrollLayout(Context context) {
this(context, null);
@@ -811,8 +812,7 @@
}
handleEmptySpaceClick(ev);
boolean expandWantsIt = false;
- if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion
- && isScrollingEnabled()) {
+ if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
if (isCancelOrUp) {
mExpandHelper.onlyObserveMovements(false);
}
@@ -1566,7 +1566,7 @@
initDownStates(ev);
handleEmptySpaceClick(ev);
boolean expandWantsIt = false;
- if (!mSwipingInProgress && !mOnlyScrollingInThisMotion && isScrollingEnabled()) {
+ if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
}
boolean scrollWantsIt = false;
@@ -2267,11 +2267,11 @@
private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
- if (row.isUserLocked()) {
+ if (row.isUserLocked() && row != getFirstChildNotGone()) {
// We are actually expanding this view
float endPosition = row.getTranslationY() + row.getActualHeight();
int stackEnd = mMaxLayoutHeight - mBottomStackPeekSize -
- mBottomStackSlowDownHeight;
+ mBottomStackSlowDownHeight + (int) mStackTranslation;
if (endPosition > stackEnd) {
mOwnScrollY += endPosition - stackEnd;
mDisallowScrollingInThisMotion = true;
@@ -2614,7 +2614,7 @@
}
}
- private boolean isBelowLastNotification(float touchX, float touchY) {
+ public boolean isBelowLastNotification(float touchX, float touchY) {
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
ExpandableView child = (ExpandableView) getChildAt(i);
@@ -2640,7 +2640,7 @@
}
}
}
- return touchY > mIntrinsicPadding;
+ return touchY > mTopPadding + mStackTranslation;
}
private void updateExpandButtons() {
@@ -2732,6 +2732,15 @@
mScrimController = scrimController;
}
+ public void forceNoOverlappingRendering(boolean force) {
+ mForceNoOverlappingRendering = force;
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
+ }
+
/**
* A listener that is notified when some child locations might have changed.
*/
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index bea138e..0a50593 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -51,6 +51,7 @@
* <a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a> developer guide.</p>
* </div>
**/
+
public class Allocation extends BaseObj {
Type mType;
Bitmap mBitmap;
@@ -455,28 +456,31 @@
*
*/
public void syncAll(int srcLocation) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "syncAll");
- switch (srcLocation) {
- case USAGE_GRAPHICS_TEXTURE:
- case USAGE_SCRIPT:
- if ((mUsage & USAGE_SHARED) != 0) {
- copyFrom(mBitmap);
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "syncAll");
+ switch (srcLocation) {
+ case USAGE_GRAPHICS_TEXTURE:
+ case USAGE_SCRIPT:
+ if ((mUsage & USAGE_SHARED) != 0) {
+ copyFrom(mBitmap);
+ }
+ break;
+ case USAGE_GRAPHICS_CONSTANTS:
+ case USAGE_GRAPHICS_VERTEX:
+ break;
+ case USAGE_SHARED:
+ if ((mUsage & USAGE_SHARED) != 0) {
+ copyTo(mBitmap);
+ }
+ break;
+ default:
+ throw new RSIllegalArgumentException("Source must be exactly one usage type.");
}
- break;
- case USAGE_GRAPHICS_CONSTANTS:
- case USAGE_GRAPHICS_VERTEX:
- break;
- case USAGE_SHARED:
- if ((mUsage & USAGE_SHARED) != 0) {
- copyTo(mBitmap);
- }
- break;
- default:
- throw new RSIllegalArgumentException("Source must be exactly one usage type.");
+ mRS.validate();
+ mRS.nAllocationSyncAll(getIDSafe(), srcLocation);
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
- mRS.validate();
- mRS.nAllocationSyncAll(getIDSafe(), srcLocation);
- Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -487,14 +491,17 @@
*
*/
public void ioSend() {
- Trace.traceBegin(RenderScript.TRACE_TAG, "ioSend");
- if ((mUsage & USAGE_IO_OUTPUT) == 0) {
- throw new RSIllegalArgumentException(
- "Can only send buffer if IO_OUTPUT usage specified.");
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "ioSend");
+ if ((mUsage & USAGE_IO_OUTPUT) == 0) {
+ throw new RSIllegalArgumentException(
+ "Can only send buffer if IO_OUTPUT usage specified.");
+ }
+ mRS.validate();
+ mRS.nAllocationIoSend(getID(mRS));
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
- mRS.validate();
- mRS.nAllocationIoSend(getID(mRS));
- Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -503,14 +510,17 @@
*
*/
public void ioReceive() {
- Trace.traceBegin(RenderScript.TRACE_TAG, "ioReceive");
- if ((mUsage & USAGE_IO_INPUT) == 0) {
- throw new RSIllegalArgumentException(
- "Can only receive if IO_INPUT usage specified.");
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "ioReceive");
+ if ((mUsage & USAGE_IO_INPUT) == 0) {
+ throw new RSIllegalArgumentException(
+ "Can only receive if IO_INPUT usage specified.");
+ }
+ mRS.validate();
+ mRS.nAllocationIoReceive(getID(mRS));
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
- mRS.validate();
- mRS.nAllocationIoReceive(getID(mRS));
- Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -519,28 +529,31 @@
* @param d Source array.
*/
public void copyFrom(BaseObj[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
- mRS.validate();
- validateIsObject();
- if (d.length != mCurrentCount) {
- throw new RSIllegalArgumentException("Array size mismatch, allocation sizeX = " +
- mCurrentCount + ", array length = " + d.length);
- }
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
+ mRS.validate();
+ validateIsObject();
+ if (d.length != mCurrentCount) {
+ throw new RSIllegalArgumentException("Array size mismatch, allocation sizeX = " +
+ mCurrentCount + ", array length = " + d.length);
+ }
- if (RenderScript.sPointerSize == 8) {
- long i[] = new long[d.length * 4];
- for (int ct=0; ct < d.length; ct++) {
- i[ct * 4] = d[ct].getID(mRS);
+ if (RenderScript.sPointerSize == 8) {
+ long i[] = new long[d.length * 4];
+ for (int ct=0; ct < d.length; ct++) {
+ i[ct * 4] = d[ct].getID(mRS);
+ }
+ copy1DRangeFromUnchecked(0, mCurrentCount, i);
+ } else {
+ int i[] = new int[d.length];
+ for (int ct=0; ct < d.length; ct++) {
+ i[ct] = (int) d[ct].getID(mRS);
+ }
+ copy1DRangeFromUnchecked(0, mCurrentCount, i);
}
- copy1DRangeFromUnchecked(0, mCurrentCount, i);
- } else {
- int i[] = new int[d.length];
- for (int ct=0; ct < d.length; ct++) {
- i[ct] = (int)d[ct].getID(mRS);
- }
- copy1DRangeFromUnchecked(0, mCurrentCount, i);
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
- Trace.traceEnd(RenderScript.TRACE_TAG);
}
private void validateBitmapFormat(Bitmap b) {
@@ -599,16 +612,19 @@
}
private void copyFromUnchecked(Object array, Element.DataType dt, int arrayLen) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
- mRS.validate();
- if (mCurrentDimZ > 0) {
- copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, array, dt, arrayLen);
- } else if (mCurrentDimY > 0) {
- copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, array, dt, arrayLen);
- } else {
- copy1DRangeFromUnchecked(0, mCurrentCount, array, dt, arrayLen);
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
+ mRS.validate();
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, array, dt, arrayLen);
+ } else if (mCurrentDimY > 0) {
+ copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, array, dt, arrayLen);
+ } else {
+ copy1DRangeFromUnchecked(0, mCurrentCount, array, dt, arrayLen);
+ }
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
- Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -619,10 +635,13 @@
* @param array The source data array
*/
public void copyFromUnchecked(Object array) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
- copyFromUnchecked(array, validateObjectIsPrimitiveArray(array, false),
- java.lang.reflect.Array.getLength(array));
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
+ copyFromUnchecked(array, validateObjectIsPrimitiveArray(array, false),
+ java.lang.reflect.Array.getLength(array));
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
+ }
}
/**
@@ -679,10 +698,13 @@
* @param array The source data array
*/
public void copyFrom(Object array) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
- copyFromUnchecked(array, validateObjectIsPrimitiveArray(array, true),
- java.lang.reflect.Array.getLength(array));
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
+ copyFromUnchecked(array, validateObjectIsPrimitiveArray(array, true),
+ java.lang.reflect.Array.getLength(array));
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
+ }
}
/**
@@ -747,19 +769,22 @@
* @param b the source bitmap
*/
public void copyFrom(Bitmap b) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
- mRS.validate();
- if (b.getConfig() == null) {
- Bitmap newBitmap = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(newBitmap);
- c.drawBitmap(b, 0, 0, null);
- copyFrom(newBitmap);
- return;
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
+ mRS.validate();
+ if (b.getConfig() == null) {
+ Bitmap newBitmap = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(newBitmap);
+ c.drawBitmap(b, 0, 0, null);
+ copyFrom(newBitmap);
+ return;
+ }
+ validateBitmapSize(b);
+ validateBitmapFormat(b);
+ mRS.nAllocationCopyFromBitmap(getID(mRS), b);
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
- validateBitmapSize(b);
- validateBitmapFormat(b);
- mRS.nAllocationCopyFromBitmap(getID(mRS), b);
- Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -769,13 +794,16 @@
* @param a the source allocation
*/
public void copyFrom(Allocation a) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
- mRS.validate();
- if (!mType.equals(a.getType())) {
- throw new RSIllegalArgumentException("Types of allocations must match.");
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
+ mRS.validate();
+ if (!mType.equals(a.getType())) {
+ throw new RSIllegalArgumentException("Types of allocations must match.");
+ }
+ copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, a, 0, 0);
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
- copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, a, 0, 0);
- Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -890,17 +918,20 @@
private void copy1DRangeFromUnchecked(int off, int count, Object array,
Element.DataType dt, int arrayLen) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
- final int dataSize = mType.mElement.getBytesSize() * count;
- // AutoPadding for Vec3 Element
- boolean usePadding = false;
- if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
- usePadding = true;
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
+ final int dataSize = mType.mElement.getBytesSize() * count;
+ // AutoPadding for Vec3 Element
+ boolean usePadding = false;
+ if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+ usePadding = true;
+ }
+ data1DChecks(off, count, arrayLen * dt.mSize, dataSize, usePadding);
+ mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, array, dataSize, dt,
+ mType.mElement.mType.mSize, usePadding);
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
- data1DChecks(off, count, arrayLen * dt.mSize, dataSize, usePadding);
- mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, array, dataSize, dt,
- mType.mElement.mType.mSize, usePadding);
- Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1074,28 +1105,31 @@
void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, Object array,
Element.DataType dt, int arrayLen) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFromUnchecked");
- mRS.validate();
- validate2DRange(xoff, yoff, w, h);
- final int dataSize = mType.mElement.getBytesSize() * w * h;
- // AutoPadding for Vec3 Element
- boolean usePadding = false;
- int sizeBytes = arrayLen * dt.mSize;
- if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
- if (dataSize / 4 * 3 > sizeBytes) {
- throw new RSIllegalArgumentException("Array too small for allocation type.");
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFromUnchecked");
+ mRS.validate();
+ validate2DRange(xoff, yoff, w, h);
+ final int dataSize = mType.mElement.getBytesSize() * w * h;
+ // AutoPadding for Vec3 Element
+ boolean usePadding = false;
+ int sizeBytes = arrayLen * dt.mSize;
+ if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+ if (dataSize / 4 * 3 > sizeBytes) {
+ throw new RSIllegalArgumentException("Array too small for allocation type.");
+ }
+ usePadding = true;
+ sizeBytes = dataSize;
+ } else {
+ if (dataSize > sizeBytes) {
+ throw new RSIllegalArgumentException("Array too small for allocation type.");
+ }
}
- usePadding = true;
- sizeBytes = dataSize;
- } else {
- if (dataSize > sizeBytes) {
- throw new RSIllegalArgumentException("Array too small for allocation type.");
- }
+ mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h,
+ array, sizeBytes, dt,
+ mType.mElement.mType.mSize, usePadding);
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
- mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h,
- array, sizeBytes, dt,
- mType.mElement.mType.mSize, usePadding);
- Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1109,11 +1143,14 @@
* @param array Data to be placed into the Allocation
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, Object array) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
- copy2DRangeFromUnchecked(xoff, yoff, w, h, array,
- validateObjectIsPrimitiveArray(array, true),
- java.lang.reflect.Array.getLength(array));
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
+ copy2DRangeFromUnchecked(xoff, yoff, w, h, array,
+ validateObjectIsPrimitiveArray(array, true),
+ java.lang.reflect.Array.getLength(array));
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
+ }
}
/**
@@ -1194,14 +1231,17 @@
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h,
Allocation data, int dataXoff, int dataYoff) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
- mRS.validate();
- validate2DRange(xoff, yoff, w, h);
- mRS.nAllocationData2D(getIDSafe(), xoff, yoff,
- mSelectedLOD, mSelectedFace.mID,
- w, h, data.getID(mRS), dataXoff, dataYoff,
- data.mSelectedLOD, data.mSelectedFace.mID);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
+ mRS.validate();
+ validate2DRange(xoff, yoff, w, h);
+ mRS.nAllocationData2D(getIDSafe(), xoff, yoff,
+ mSelectedLOD, mSelectedFace.mID,
+ w, h, data.getID(mRS), dataXoff, dataYoff,
+ data.mSelectedLOD, data.mSelectedFace.mID);
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
+ }
}
/**
@@ -1258,28 +1298,31 @@
*/
private void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d,
Object array, Element.DataType dt, int arrayLen) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy3DRangeFromUnchecked");
- mRS.validate();
- validate3DRange(xoff, yoff, zoff, w, h, d);
- final int dataSize = mType.mElement.getBytesSize() * w * h * d;
- // AutoPadding for Vec3 Element
- boolean usePadding = false;
- int sizeBytes = arrayLen * dt.mSize;
- if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
- if (dataSize / 4 * 3 > sizeBytes) {
- throw new RSIllegalArgumentException("Array too small for allocation type.");
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy3DRangeFromUnchecked");
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ final int dataSize = mType.mElement.getBytesSize() * w * h * d;
+ // AutoPadding for Vec3 Element
+ boolean usePadding = false;
+ int sizeBytes = arrayLen * dt.mSize;
+ if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+ if (dataSize / 4 * 3 > sizeBytes) {
+ throw new RSIllegalArgumentException("Array too small for allocation type.");
+ }
+ usePadding = true;
+ sizeBytes = dataSize;
+ } else {
+ if (dataSize > sizeBytes) {
+ throw new RSIllegalArgumentException("Array too small for allocation type.");
+ }
}
- usePadding = true;
- sizeBytes = dataSize;
- } else {
- if (dataSize > sizeBytes) {
- throw new RSIllegalArgumentException("Array too small for allocation type.");
- }
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD, w, h, d,
+ array, sizeBytes, dt,
+ mType.mElement.mType.mSize, usePadding);
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
- mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD, w, h, d,
- array, sizeBytes, dt,
- mType.mElement.mType.mSize, usePadding);
- Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1295,11 +1338,14 @@
* @param array to be placed into the allocation
*/
public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, Object array) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy3DRangeFrom");
- copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, array,
- validateObjectIsPrimitiveArray(array, true),
- java.lang.reflect.Array.getLength(array));
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy3DRangeFrom");
+ copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, array,
+ validateObjectIsPrimitiveArray(array, true),
+ java.lang.reflect.Array.getLength(array));
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
+ }
}
/**
@@ -1334,34 +1380,40 @@
* @param b The bitmap to be set from the Allocation.
*/
public void copyTo(Bitmap b) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
- mRS.validate();
- validateBitmapFormat(b);
- validateBitmapSize(b);
- mRS.nAllocationCopyToBitmap(getID(mRS), b);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
+ mRS.validate();
+ validateBitmapFormat(b);
+ validateBitmapSize(b);
+ mRS.nAllocationCopyToBitmap(getID(mRS), b);
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
+ }
}
private void copyTo(Object array, Element.DataType dt, int arrayLen) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
- mRS.validate();
- boolean usePadding = false;
- if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
- usePadding = true;
- }
- if (usePadding) {
- if (dt.mSize * arrayLen < mSize / 4 * 3) {
- throw new RSIllegalArgumentException(
- "Size of output array cannot be smaller than size of allocation.");
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
+ mRS.validate();
+ boolean usePadding = false;
+ if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+ usePadding = true;
}
- } else {
- if (dt.mSize * arrayLen < mSize) {
- throw new RSIllegalArgumentException(
- "Size of output array cannot be smaller than size of allocation.");
+ if (usePadding) {
+ if (dt.mSize * arrayLen < mSize / 4 * 3) {
+ throw new RSIllegalArgumentException(
+ "Size of output array cannot be smaller than size of allocation.");
+ }
+ } else {
+ if (dt.mSize * arrayLen < mSize) {
+ throw new RSIllegalArgumentException(
+ "Size of output array cannot be smaller than size of allocation.");
+ }
}
+ mRS.nAllocationRead(getID(mRS), array, dt, mType.mElement.mType.mSize, usePadding);
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
- mRS.nAllocationRead(getID(mRS), array, dt, mType.mElement.mType.mSize, usePadding);
- Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1499,17 +1551,20 @@
private void copy1DRangeToUnchecked(int off, int count, Object array,
Element.DataType dt, int arrayLen) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeToUnchecked");
- final int dataSize = mType.mElement.getBytesSize() * count;
- // AutoPadding for Vec3 Element
- boolean usePadding = false;
- if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
- usePadding = true;
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeToUnchecked");
+ final int dataSize = mType.mElement.getBytesSize() * count;
+ // AutoPadding for Vec3 Element
+ boolean usePadding = false;
+ if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+ usePadding = true;
+ }
+ data1DChecks(off, count, arrayLen * dt.mSize, dataSize, usePadding);
+ mRS.nAllocationRead1D(getIDSafe(), off, mSelectedLOD, count, array, dataSize, dt,
+ mType.mElement.mType.mSize, usePadding);
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
- data1DChecks(off, count, arrayLen * dt.mSize, dataSize, usePadding);
- mRS.nAllocationRead1D(getIDSafe(), off, mSelectedLOD, count, array, dataSize, dt,
- mType.mElement.mType.mSize, usePadding);
- Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1649,27 +1704,30 @@
void copy2DRangeToUnchecked(int xoff, int yoff, int w, int h, Object array,
Element.DataType dt, int arrayLen) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeToUnchecked");
- mRS.validate();
- validate2DRange(xoff, yoff, w, h);
- final int dataSize = mType.mElement.getBytesSize() * w * h;
- // AutoPadding for Vec3 Element
- boolean usePadding = false;
- int sizeBytes = arrayLen * dt.mSize;
- if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
- if (dataSize / 4 * 3 > sizeBytes) {
- throw new RSIllegalArgumentException("Array too small for allocation type.");
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeToUnchecked");
+ mRS.validate();
+ validate2DRange(xoff, yoff, w, h);
+ final int dataSize = mType.mElement.getBytesSize() * w * h;
+ // AutoPadding for Vec3 Element
+ boolean usePadding = false;
+ int sizeBytes = arrayLen * dt.mSize;
+ if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+ if (dataSize / 4 * 3 > sizeBytes) {
+ throw new RSIllegalArgumentException("Array too small for allocation type.");
+ }
+ usePadding = true;
+ sizeBytes = dataSize;
+ } else {
+ if (dataSize > sizeBytes) {
+ throw new RSIllegalArgumentException("Array too small for allocation type.");
+ }
}
- usePadding = true;
- sizeBytes = dataSize;
- } else {
- if (dataSize > sizeBytes) {
- throw new RSIllegalArgumentException("Array too small for allocation type.");
- }
+ mRS.nAllocationRead2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h,
+ array, sizeBytes, dt, mType.mElement.mType.mSize, usePadding);
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
- mRS.nAllocationRead2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h,
- array, sizeBytes, dt, mType.mElement.mType.mSize, usePadding);
- Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1757,27 +1815,30 @@
*/
private void copy3DRangeToUnchecked(int xoff, int yoff, int zoff, int w, int h, int d,
Object array, Element.DataType dt, int arrayLen) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy3DRangeToUnchecked");
- mRS.validate();
- validate3DRange(xoff, yoff, zoff, w, h, d);
- final int dataSize = mType.mElement.getBytesSize() * w * h * d;
- // AutoPadding for Vec3 Element
- boolean usePadding = false;
- int sizeBytes = arrayLen * dt.mSize;
- if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
- if (dataSize / 4 * 3 > sizeBytes) {
- throw new RSIllegalArgumentException("Array too small for allocation type.");
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy3DRangeToUnchecked");
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ final int dataSize = mType.mElement.getBytesSize() * w * h * d;
+ // AutoPadding for Vec3 Element
+ boolean usePadding = false;
+ int sizeBytes = arrayLen * dt.mSize;
+ if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+ if (dataSize / 4 * 3 > sizeBytes) {
+ throw new RSIllegalArgumentException("Array too small for allocation type.");
+ }
+ usePadding = true;
+ sizeBytes = dataSize;
+ } else {
+ if (dataSize > sizeBytes) {
+ throw new RSIllegalArgumentException("Array too small for allocation type.");
+ }
}
- usePadding = true;
- sizeBytes = dataSize;
- } else {
- if (dataSize > sizeBytes) {
- throw new RSIllegalArgumentException("Array too small for allocation type.");
- }
+ mRS.nAllocationRead3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD, w, h, d,
+ array, sizeBytes, dt, mType.mElement.mType.mSize, usePadding);
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
- mRS.nAllocationRead3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD, w, h, d,
- array, sizeBytes, dt, mType.mElement.mType.mSize, usePadding);
- Trace.traceEnd(RenderScript.TRACE_TAG);
}
/*
@@ -1815,17 +1876,20 @@
* utilized
*/
static public Allocation createTyped(RenderScript rs, Type type, MipmapControl mips, int usage) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "createTyped");
- rs.validate();
- if (type.getID(rs) == 0) {
- throw new RSInvalidStateException("Bad Type");
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "createTyped");
+ rs.validate();
+ if (type.getID(rs) == 0) {
+ throw new RSInvalidStateException("Bad Type");
+ }
+ long id = rs.nAllocationCreateTyped(type.getID(rs), mips.mID, usage, 0);
+ if (id == 0) {
+ throw new RSRuntimeException("Allocation creation failed.");
+ }
+ return new Allocation(id, rs, type, usage);
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
- long id = rs.nAllocationCreateTyped(type.getID(rs), mips.mID, usage, 0);
- if (id == 0) {
- throw new RSRuntimeException("Allocation creation failed.");
- }
- Trace.traceEnd(RenderScript.TRACE_TAG);
- return new Allocation(id, rs, type, usage);
}
/**
@@ -1869,18 +1933,21 @@
*/
static public Allocation createSized(RenderScript rs, Element e,
int count, int usage) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "createSized");
- rs.validate();
- Type.Builder b = new Type.Builder(rs, e);
- b.setX(count);
- Type t = b.create();
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "createSized");
+ rs.validate();
+ Type.Builder b = new Type.Builder(rs, e);
+ b.setX(count);
+ Type t = b.create();
- long id = rs.nAllocationCreateTyped(t.getID(rs), MipmapControl.MIPMAP_NONE.mID, usage, 0);
- if (id == 0) {
- throw new RSRuntimeException("Allocation creation failed.");
+ long id = rs.nAllocationCreateTyped(t.getID(rs), MipmapControl.MIPMAP_NONE.mID, usage, 0);
+ if (id == 0) {
+ throw new RSRuntimeException("Allocation creation failed.");
+ }
+ return new Allocation(id, rs, t, usage);
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
- Trace.traceEnd(RenderScript.TRACE_TAG);
- return new Allocation(id, rs, t, usage);
}
/**
@@ -1939,44 +2006,47 @@
static public Allocation createFromBitmap(RenderScript rs, Bitmap b,
MipmapControl mips,
int usage) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "createFromBitmap");
- rs.validate();
+ try {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "createFromBitmap");
+ rs.validate();
- // WAR undocumented color formats
- if (b.getConfig() == null) {
- if ((usage & USAGE_SHARED) != 0) {
- throw new RSIllegalArgumentException("USAGE_SHARED cannot be used with a Bitmap that has a null config.");
+ // WAR undocumented color formats
+ if (b.getConfig() == null) {
+ if ((usage & USAGE_SHARED) != 0) {
+ throw new RSIllegalArgumentException("USAGE_SHARED cannot be used with a Bitmap that has a null config.");
+ }
+ Bitmap newBitmap = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(newBitmap);
+ c.drawBitmap(b, 0, 0, null);
+ return createFromBitmap(rs, newBitmap, mips, usage);
}
- Bitmap newBitmap = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(newBitmap);
- c.drawBitmap(b, 0, 0, null);
- return createFromBitmap(rs, newBitmap, mips, usage);
- }
- Type t = typeFromBitmap(rs, b, mips);
+ Type t = typeFromBitmap(rs, b, mips);
- // enable optimized bitmap path only with no mipmap and script-only usage
- if (mips == MipmapControl.MIPMAP_NONE &&
- t.getElement().isCompatible(Element.RGBA_8888(rs)) &&
- usage == (USAGE_SHARED | USAGE_SCRIPT | USAGE_GRAPHICS_TEXTURE)) {
- long id = rs.nAllocationCreateBitmapBackedAllocation(t.getID(rs), mips.mID, b, usage);
+ // enable optimized bitmap path only with no mipmap and script-only usage
+ if (mips == MipmapControl.MIPMAP_NONE &&
+ t.getElement().isCompatible(Element.RGBA_8888(rs)) &&
+ usage == (USAGE_SHARED | USAGE_SCRIPT | USAGE_GRAPHICS_TEXTURE)) {
+ long id = rs.nAllocationCreateBitmapBackedAllocation(t.getID(rs), mips.mID, b, usage);
+ if (id == 0) {
+ throw new RSRuntimeException("Load failed.");
+ }
+
+ // keep a reference to the Bitmap around to prevent GC
+ Allocation alloc = new Allocation(id, rs, t, usage);
+ alloc.setBitmap(b);
+ return alloc;
+ }
+
+
+ long id = rs.nAllocationCreateFromBitmap(t.getID(rs), mips.mID, b, usage);
if (id == 0) {
throw new RSRuntimeException("Load failed.");
}
-
- // keep a reference to the Bitmap around to prevent GC
- Allocation alloc = new Allocation(id, rs, t, usage);
- alloc.setBitmap(b);
- return alloc;
+ return new Allocation(id, rs, t, usage);
+ } finally {
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
-
-
- long id = rs.nAllocationCreateFromBitmap(t.getID(rs), mips.mID, b, usage);
- if (id == 0) {
- throw new RSRuntimeException("Load failed.");
- }
- Trace.traceEnd(RenderScript.TRACE_TAG);
- return new Allocation(id, rs, t, usage);
}
/**
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2e4b881..44d00d7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1661,6 +1661,12 @@
}
private static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208";
+ private static final String DEFAULT_TCP_RWND_KEY = "net.tcp.default_init_rwnd";
+
+ // Overridden for testing purposes to avoid writing to SystemProperties.
+ protected int getDefaultTcpRwnd() {
+ return SystemProperties.getInt(DEFAULT_TCP_RWND_KEY, 0);
+ }
private void updateTcpBufferSizes(NetworkAgentInfo nai) {
if (isDefaultNetwork(nai) == false) {
@@ -1696,10 +1702,8 @@
loge("Can't set TCP buffer sizes:" + e);
}
- final String defaultRwndKey = "net.tcp.default_init_rwnd";
- int defaultRwndValue = SystemProperties.getInt(defaultRwndKey, 0);
Integer rwndValue = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.TCP_DEFAULT_INIT_RWND, defaultRwndValue);
+ Settings.Global.TCP_DEFAULT_INIT_RWND, getDefaultTcpRwnd());
final String sysctlKey = "sys.sysctl.tcp_def_init_rwnd";
if (rwndValue != 0) {
SystemProperties.set(sysctlKey, rwndValue.toString());
@@ -4505,7 +4509,7 @@
info.setDetailedState(DetailedState.CONNECTED, null, info.getExtraInfo());
sendConnectedBroadcast(info);
} else {
- info.setDetailedState(DetailedState.DISCONNECTED, null, info.getExtraInfo());
+ info.setDetailedState(DetailedState.DISCONNECTED, info.getReason(), info.getExtraInfo());
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 201a4ca..69405cf 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -428,19 +428,39 @@
private final IPackageManager mIPackageManager;
class SettingsObserver extends ContentObserver {
+ int mUserId;
+ boolean mRegistered = false;
String mLastEnabled = "";
+ /**
+ * <em>This constructor must be called within the lock.</em>
+ */
SettingsObserver(Handler handler) {
super(handler);
+ }
+
+ public void registerContentObserverLocked(int userId) {
+ if (mRegistered && mUserId == userId) {
+ return;
+ }
ContentResolver resolver = mContext.getContentResolver();
+ if (mRegistered) {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ mRegistered = false;
+ }
+ if (mUserId != userId) {
+ mLastEnabled = "";
+ mUserId = userId;
+ }
resolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
+ Settings.Secure.DEFAULT_INPUT_METHOD), false, this, userId);
resolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.ENABLED_INPUT_METHODS), false, this);
+ Settings.Secure.ENABLED_INPUT_METHODS), false, this, userId);
resolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this);
+ Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this, userId);
resolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this);
+ Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this, userId);
+ mRegistered = true;
}
@Override public void onChange(boolean selfChange, Uri uri) {
@@ -460,6 +480,12 @@
}
}
}
+
+ @Override
+ public String toString() {
+ return "SettingsObserver{mUserId=" + mUserId + " mRegistered=" + mRegistered
+ + " mLastEnabled=" + mLastEnabled + "}";
+ }
}
class ImmsBroadcastReceiver extends android.content.BroadcastReceiver {
@@ -756,6 +782,8 @@
mContext = context;
mRes = context.getResources();
mHandler = new Handler(this);
+ // Note: SettingsObserver doesn't register observers in its constructor.
+ mSettingsObserver = new SettingsObserver(mHandler);
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() {
@@ -860,8 +888,8 @@
}
}
- mSettingsObserver = new SettingsObserver(mHandler);
synchronized (mMethodMap) {
+ mSettingsObserver.registerContentObserverLocked(userId);
updateFromSettingsLocked(true);
}
@@ -960,26 +988,35 @@
}
private void switchUserLocked(int newUserId) {
+ if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
+ + " currentUserId=" + mSettings.getCurrentUserId());
+
+ // ContentObserver should be registered again when the user is changed
+ mSettingsObserver.registerContentObserverLocked(newUserId);
mSettings.setCurrentUserId(newUserId);
updateCurrentProfileIds();
// InputMethodFileManager should be reset when the user is changed
mFileManager = new InputMethodFileManager(mMethodMap, newUserId);
final String defaultImiId = mSettings.getSelectedInputMethod();
+
+ if (DEBUG) Slog.d(TAG, "Switching user stage 2/3. newUserId=" + newUserId
+ + " defaultImiId=" + defaultImiId);
+
// For secondary users, the list of enabled IMEs may not have been updated since the
// callbacks to PackageMonitor are ignored for the secondary user. Here, defaultImiId may
// not be empty even if the IME has been uninstalled by the primary user.
// Even in such cases, IMMS works fine because it will find the most applicable
// IME for that user.
final boolean initialUserSwitch = TextUtils.isEmpty(defaultImiId);
- if (DEBUG) {
- Slog.d(TAG, "Switch user: " + newUserId + " current ime = " + defaultImiId);
- }
resetAllInternalStateLocked(false /* updateOnlyWhenLocaleChanged */,
initialUserSwitch /* needsToResetDefaultIme */);
if (initialUserSwitch) {
InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mContext.getPackageManager(),
mSettings.getEnabledInputMethodListLocked());
}
+
+ if (DEBUG) Slog.d(TAG, "Switching user stage 3/3. newUserId=" + newUserId
+ + " selectedIme=" + mSettings.getSelectedInputMethod());
}
void updateCurrentProfileIds() {
@@ -3706,6 +3743,9 @@
p.println(" mCurUserActionNotificationSequenceNumber="
+ mCurUserActionNotificationSequenceNumber);
p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mScreenOn);
+ p.println(" mSettingsObserver=" + mSettingsObserver);
+ p.println(" mSwitchingController:");
+ mSwitchingController.dump(p);
}
p.println(" ");
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index d48953d..8c8be4e 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -180,6 +180,7 @@
private static final String TAG_STORAGE_BENCHMARK = "storage_benchmark";
private static final String VOLD_TAG = "VoldConnector";
+ private static final String CRYPTD_TAG = "CryptdConnector";
/** Maximum number of ASEC containers allowed to be mounted. */
private static final int MAX_CONTAINERS = 250;
@@ -367,6 +368,7 @@
private final Context mContext;
private final NativeDaemonConnector mConnector;
+ private final NativeDaemonConnector mCryptConnector;
private volatile boolean mSystemReady = false;
private volatile boolean mDaemonConnected = false;
@@ -375,7 +377,8 @@
private final Callbacks mCallbacks;
- private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
+ // Two connectors - mConnector & mCryptConnector
+ private final CountDownLatch mConnectedSignal = new CountDownLatch(2);
private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
private final Object mUnmountLock = new Object();
@@ -754,6 +757,10 @@
* the hounds!
*/
mConnectedSignal.countDown();
+ if (mConnectedSignal.getCount() != 0) {
+ // More daemons need to connect
+ return;
+ }
// On an encrypted device we can't see system properties yet, so pull
// the system locale out of the mount service.
@@ -1186,6 +1193,7 @@
* amount of containers we'd ever expect to have. This keeps an
* "asec list" from blocking a thread repeatedly.
*/
+
mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
null);
mConnector.setDebug(true);
@@ -1193,6 +1201,14 @@
Thread thread = new Thread(mConnector, VOLD_TAG);
thread.start();
+ // Reuse parameters from first connector since they are tested and safe
+ mCryptConnector = new NativeDaemonConnector(this, "cryptd",
+ MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);
+ mCryptConnector.setDebug(true);
+
+ Thread crypt_thread = new Thread(mCryptConnector, CRYPTD_TAG);
+ crypt_thread.start();
+
// Add ourself to the Watchdog monitors if enabled.
if (WATCHDOG_ENABLE) {
Watchdog.getInstance().addMonitor(this);
@@ -2049,7 +2065,7 @@
final NativeDaemonEvent event;
try {
- event = mConnector.execute("cryptfs", "cryptocomplete");
+ event = mCryptConnector.execute("cryptfs", "cryptocomplete");
return Integer.parseInt(event.getMessage());
} catch (NumberFormatException e) {
// Bad result - unexpected.
@@ -2096,7 +2112,7 @@
final NativeDaemonEvent event;
try {
- event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(toHex(password)));
+ event = mCryptConnector.execute("cryptfs", "checkpw", new SensitiveArg(toHex(password)));
final int code = Integer.parseInt(event.getMessage());
if (code == 0) {
@@ -2105,7 +2121,7 @@
mHandler.postDelayed(new Runnable() {
public void run() {
try {
- mConnector.execute("cryptfs", "restart");
+ mCryptConnector.execute("cryptfs", "restart");
} catch (NativeDaemonConnectorException e) {
Slog.e(TAG, "problem executing in background", e);
}
@@ -2135,7 +2151,7 @@
}
try {
- mConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
+ mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
new SensitiveArg(toHex(password)));
} catch (NativeDaemonConnectorException e) {
// Encryption failed
@@ -2160,7 +2176,7 @@
}
try {
- NativeDaemonEvent event = mConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
+ NativeDaemonEvent event = mCryptConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
new SensitiveArg(toHex(password)));
return Integer.parseInt(event.getMessage());
} catch (NativeDaemonConnectorException e) {
@@ -2194,7 +2210,7 @@
final NativeDaemonEvent event;
try {
- event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(toHex(password)));
+ event = mCryptConnector.execute("cryptfs", "verifypw", new SensitiveArg(toHex(password)));
Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
return Integer.parseInt(event.getMessage());
} catch (NativeDaemonConnectorException e) {
@@ -2214,7 +2230,7 @@
final NativeDaemonEvent event;
try {
- event = mConnector.execute("cryptfs", "getpwtype");
+ event = mCryptConnector.execute("cryptfs", "getpwtype");
for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
if (CRYPTO_TYPES[i].equals(event.getMessage()))
return i;
@@ -2238,7 +2254,7 @@
final NativeDaemonEvent event;
try {
- event = mConnector.execute("cryptfs", "setfield", field, contents);
+ event = mCryptConnector.execute("cryptfs", "setfield", field, contents);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
@@ -2257,7 +2273,7 @@
final NativeDaemonEvent event;
try {
final String[] contents = NativeDaemonEvent.filterMessageList(
- mConnector.executeForList("cryptfs", "getfield", field),
+ mCryptConnector.executeForList("cryptfs", "getfield", field),
VoldResponseCode.CryptfsGetfieldResult);
String result = new String();
for (String content : contents) {
@@ -2279,7 +2295,7 @@
final NativeDaemonEvent event;
try {
- event = mConnector.execute("cryptfs", "getpw");
+ event = mCryptConnector.execute("cryptfs", "getpw");
if ("-1".equals(event.getMessage())) {
// -1 equals no password
return null;
@@ -2301,7 +2317,7 @@
final NativeDaemonEvent event;
try {
- event = mConnector.execute("cryptfs", "clearpw");
+ event = mCryptConnector.execute("cryptfs", "clearpw");
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
@@ -3114,5 +3130,8 @@
if (mConnector != null) {
mConnector.monitor();
}
+ if (mCryptConnector != null) {
+ mCryptConnector.monitor();
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 81b8457..64e30e5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -39,13 +39,13 @@
import android.Manifest;
import android.app.AppOpsManager;
import android.app.ApplicationThreadNative;
-import android.app.AssistContent;
-import android.app.AssistStructure;
import android.app.IActivityContainer;
import android.app.IActivityContainerCallback;
import android.app.IAppTask;
import android.app.ITaskStackListener;
import android.app.ProfilerInfo;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index bf896a5..fc50e2c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -32,6 +32,7 @@
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.Message;
+import android.os.Process;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -48,6 +49,7 @@
import android.telephony.TelephonyManager;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -177,8 +179,8 @@
private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = BASE + 11;
private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
- // Default to 30s linger time-out.
- private static final int DEFAULT_LINGER_DELAY_MS = 30000;
+ // Default to 30s linger time-out. Modifyable only for testing.
+ private static int DEFAULT_LINGER_DELAY_MS = 30000;
private final int mLingerDelayMs;
private int mLingerToken = 0;
@@ -771,4 +773,13 @@
mContext.sendBroadcastAsUser(latencyBroadcast, UserHandle.CURRENT,
PERMISSION_ACCESS_NETWORK_CONDITIONS);
}
+
+ // Allow tests to override linger time.
+ @VisibleForTesting
+ public static void SetDefaultLingerTime(int time_ms) {
+ if (Process.myUid() == Process.SYSTEM_UID) {
+ throw new SecurityException("SetDefaultLingerTime only for internal testing.");
+ }
+ DEFAULT_LINGER_DELAY_MS = time_ms;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index dcf668d..1cec750 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -52,6 +52,7 @@
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SELinux;
import android.os.UserHandle;
import android.system.ErrnoException;
import android.system.Os;
@@ -77,6 +78,7 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@@ -163,6 +165,8 @@
private final List<File> mResolvedStagedFiles = new ArrayList<>();
@GuardedBy("mLock")
private final List<File> mResolvedInheritedFiles = new ArrayList<>();
+ @GuardedBy("mLock")
+ private File mInheritedFilesBase;
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
@@ -511,8 +515,13 @@
final List<File> fromFiles = mResolvedInheritedFiles;
final File toDir = resolveStageDir();
+ if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
+ if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
+ throw new IllegalStateException("mInheritedFilesBase == null");
+ }
+
if (isLinkPossible(fromFiles, toDir)) {
- linkFiles(fromFiles, toDir);
+ createDirsAndLinkFiles(fromFiles, toDir, mInheritedFilesBase);
} else {
// TODO: this should delegate to DCS so the system process
// avoids holding open FDs into containers.
@@ -690,6 +699,31 @@
}
}
}
+
+ // Inherit compiled oat directory.
+ final File packageInstallDir = (new File(app.getBaseCodePath())).getParentFile();
+ mInheritedFilesBase = packageInstallDir;
+ final File oatDir = new File(packageInstallDir, "oat");
+ if (oatDir.exists()) {
+ final File[] archSubdirs = oatDir.listFiles();
+ // Only add "oatDir" if it contains arch specific subdirs.
+ if (archSubdirs != null && archSubdirs.length > 0) {
+ mResolvedInheritedFiles.add(oatDir);
+ }
+ final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();
+ for (File archSubDir : archSubdirs) {
+ // Skip any directory that isn't an ISA subdir.
+ if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
+ continue;
+ }
+
+ List<File> oatFiles = Arrays.asList(archSubDir.listFiles());
+ if (!oatFiles.isEmpty()) {
+ mResolvedInheritedFiles.add(archSubDir);
+ mResolvedInheritedFiles.addAll(oatFiles);
+ }
+ }
+ }
}
}
@@ -768,14 +802,69 @@
return true;
}
- private static void linkFiles(List<File> fromFiles, File toDir) throws IOException {
+ /**
+ * Reparents the path of {@code file} from {@code oldBase} to {@code newBase}. {@code file}
+ * must necessarily be a subpath of {@code oldBase}. It is an error for {@code file} to have
+ * relative path components such as {@code "."} or {@code ".."}. For example, for we will
+ * reparent {@code /foo/bar/baz} to {@code /foo2/bar/baz} if {@code oldBase} was {@code /foo}
+ * and {@code newBase} was {@code /foo2}.
+ */
+ private static File reparentPath(File file, File oldBase, File newBase) throws IOException {
+ final String oldBaseStr = oldBase.getAbsolutePath();
+ final String pathStr = file.getAbsolutePath();
+
+ // Don't allow relative paths.
+ if (pathStr.contains("/.") ) {
+ throw new IOException("Invalid path (was relative) : " + pathStr);
+ }
+
+ if (pathStr.startsWith(oldBaseStr)) {
+ final String relative = pathStr.substring(oldBaseStr.length());
+ return new File(newBase, relative);
+ }
+
+ throw new IOException("File: " + pathStr + " outside base: " + oldBaseStr);
+ }
+
+ /**
+ * Recreates a directory and file structure, specified by a list of files {@code fromFiles}
+ * which are subpaths of {@code fromDir} to {@code toDir}. Directories are created with the
+ * same permissions, and regular files are linked.
+ *
+ * TODO: Move this function to installd so that the system process doesn't have to
+ * manipulate / relabel directories.
+ */
+ private static void createDirsAndLinkFiles(List<File> fromFiles, File toDir, File fromDir)
+ throws IOException {
for (File fromFile : fromFiles) {
- final File toFile = new File(toDir, fromFile.getName());
+ final File toFile = reparentPath(fromFile, fromDir, toDir);
+ final StructStat stat;
try {
- if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile);
- Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath());
+ stat = Os.stat(fromFile.getAbsolutePath());
} catch (ErrnoException e) {
- throw new IOException("Failed to link " + fromFile + " to " + toFile, e);
+ throw new IOException("Failed to stat: " + fromFile.getAbsolutePath(), e);
+ }
+
+ if (OsConstants.S_ISDIR(stat.st_mode)) {
+ if (LOGD) Slog.d(TAG, "Creating directory " + toFile.getAbsolutePath());
+ try {
+ Os.mkdir(toFile.getAbsolutePath(), stat.st_mode);
+ } catch (ErrnoException e) {
+ throw new IOException("Failed to create dir: " + toFile.getAbsolutePath(), e);
+ }
+
+ // We do this to ensure that the oat/ directory is created with the right
+ // label (data_dalvikcache_file) instead of apk_tmpfile.
+ if (!SELinux.restorecon(toFile)) {
+ throw new IOException("Failed to restorecon: " + toFile.getAbsolutePath());
+ }
+ } else {
+ if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile);
+ try {
+ Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath());
+ } catch (ErrnoException e) {
+ throw new IOException("Failed to link " + fromFile + " to " + toFile, e);
+ }
}
}
Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 47e3963..e370afc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1876,7 +1876,7 @@
alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");
/**
- * And there are a number of commands implemented in Java, which
+ * There are a number of commands implemented in Java, which
* we currently need to do the dexopt on so that they can be
* run from a non-root shell.
*/
@@ -6361,63 +6361,39 @@
final String path = scanFile.getPath();
final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
- if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
- setBundledAppAbisAndRoots(pkg, pkgSetting);
- // If we haven't found any native libraries for the app, check if it has
- // renderscript code. We'll need to force the app to 32 bit if it has
- // renderscript bitcode.
- if (pkg.applicationInfo.primaryCpuAbi == null
- && pkg.applicationInfo.secondaryCpuAbi == null
- && Build.SUPPORTED_64_BIT_ABIS.length > 0) {
- NativeLibraryHelper.Handle handle = null;
- try {
- handle = NativeLibraryHelper.Handle.create(scanFile);
- if (NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
- pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
- }
- } catch (IOException ioe) {
- Slog.w(TAG, "Error scanning system app : " + ioe);
- } finally {
- IoUtils.closeQuietly(handle);
- }
- }
-
- setNativeLibraryPaths(pkg);
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
+ derivePackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */);
} else {
- if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
- deriveNonSystemPackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */);
- } else {
- if ((scanFlags & SCAN_MOVE) != 0) {
- // We haven't run dex-opt for this move (since we've moved the compiled output too)
- // but we already have this packages package info in the PackageSetting. We just
- // use that and derive the native library path based on the new codepath.
- pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;
- pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString;
- }
-
- // Set native library paths again. For moves, the path will be updated based on the
- // ABIs we've determined above. For non-moves, the path will be updated based on the
- // ABIs we determined during compilation, but the path will depend on the final
- // package path (after the rename away from the stage path).
- setNativeLibraryPaths(pkg);
+ if ((scanFlags & SCAN_MOVE) != 0) {
+ // We haven't run dex-opt for this move (since we've moved the compiled output too)
+ // but we already have this packages package info in the PackageSetting. We just
+ // use that and derive the native library path based on the new codepath.
+ pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;
+ pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString;
}
- if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
- final int[] userIds = sUserManager.getUserIds();
- synchronized (mInstallLock) {
- // Create a native library symlink only if we have native libraries
- // and if the native libraries are 32 bit libraries. We do not provide
- // this symlink for 64 bit libraries.
- if (pkg.applicationInfo.primaryCpuAbi != null &&
- !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
- final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
- for (int userId : userIds) {
- if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
- nativeLibPath, userId) < 0) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Failed linking native library dir (user=" + userId + ")");
- }
+ // Set native library paths again. For moves, the path will be updated based on the
+ // ABIs we've determined above. For non-moves, the path will be updated based on the
+ // ABIs we determined during compilation, but the path will depend on the final
+ // package path (after the rename away from the stage path).
+ setNativeLibraryPaths(pkg);
+ }
+
+ if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
+ final int[] userIds = sUserManager.getUserIds();
+ synchronized (mInstallLock) {
+ // Create a native library symlink only if we have native libraries
+ // and if the native libraries are 32 bit libraries. We do not provide
+ // this symlink for 64 bit libraries.
+ if (pkg.applicationInfo.primaryCpuAbi != null &&
+ !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
+ final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
+ for (int userId : userIds) {
+ if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
+ nativeLibPath, userId) < 0) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Failed linking native library dir (user=" + userId + ")");
}
}
}
@@ -6946,8 +6922,8 @@
*
* If {@code extractLibs} is true, native libraries are extracted from the app if required.
*/
- public void deriveNonSystemPackageAbi(PackageParser.Package pkg, File scanFile,
- String cpuAbiOverride, boolean extractLibs)
+ public void derivePackageAbi(PackageParser.Package pkg, File scanFile,
+ String cpuAbiOverride, boolean extractLibs)
throws PackageManagerException {
// TODO: We can probably be smarter about this stuff. For installed apps,
// we can calculate this information at install time once and for all. For
@@ -6959,8 +6935,10 @@
setNativeLibraryPaths(pkg);
// We would never need to extract libs for forward-locked and external packages,
- // since the container service will do it for us.
- if (pkg.isForwardLocked() || isExternal(pkg)) {
+ // since the container service will do it for us. We shouldn't attempt to
+ // extract libs from system app when it was not updated.
+ if (pkg.isForwardLocked() || isExternal(pkg) ||
+ (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) ) {
extractLibs = false;
}
@@ -7291,33 +7269,6 @@
}
/**
- * Calculate the abis and roots for a bundled app. These can uniquely
- * be determined from the contents of the system partition, i.e whether
- * it contains 64 or 32 bit shared libraries etc. We do not validate any
- * of this information, and instead assume that the system was built
- * sensibly.
- */
- private void setBundledAppAbisAndRoots(PackageParser.Package pkg,
- PackageSetting pkgSetting) {
- final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());
-
- // If "/system/lib64/apkname" exists, assume that is the per-package
- // native library directory to use; otherwise use "/system/lib/apkname".
- final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir);
- setBundledAppAbi(pkg, apkRoot, apkName);
- // pkgSetting might be null during rescan following uninstall of updates
- // to a bundled app, so accommodate that possibility. The settings in
- // that case will be established later from the parsed package.
- //
- // If the settings aren't null, sync them up with what we've just derived.
- // note that apkRoot isn't stored in the package settings.
- if (pkgSetting != null) {
- pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
- pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
- }
- }
-
- /**
* Deduces the ABI of a bundled app and sets the relevant fields on the
* parsed pkg object.
*
@@ -11504,7 +11455,7 @@
final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
|| (args.volumeUuid != null));
boolean replace = false;
- int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;
+ int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
// Result object to be returned
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
@@ -11695,7 +11646,7 @@
scanFlags |= SCAN_NO_DEX;
try {
- deriveNonSystemPackageAbi(pkg, new File(pkg.codePath), args.abiOverride,
+ derivePackageAbi(pkg, new File(pkg.codePath), args.abiOverride,
true /* extract libs */);
} catch (PackageManagerException pme) {
Slog.e(TAG, "Error deriving application ABI", pme);
@@ -11705,7 +11656,7 @@
// Run dexopt before old package gets removed, to minimize time when app is unavailable
int result = mPackageDexOptimizer
- .performDexOpt(pkg, null /* instruction sets */, true /* forceDex */,
+ .performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,
false /* defer */, false /* inclDependencies */);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7019453..6b5c224 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2734,14 +2734,16 @@
}
}
final AppWindowToken appToken = win.mAppToken;
+ // Prevent an immediate window exit only for a real animation, ignoring e.g.
+ // dummy animations.
+ final boolean inAnimation = win.mWinAnimator.isWindowAnimatingNow();
// The starting window is the last window in this app token and it isn't animating.
// Allow it to be removed now as there is no additional window or animation that will
// trigger its removal.
final boolean lastWinStartingNotAnimating = startingWindow && appToken!= null
- && appToken.allAppWindows.size() == 1 && !win.mWinAnimator.isWindowAnimating();
- if (!lastWinStartingNotAnimating && (win.mExiting || win.mWinAnimator.isAnimating())) {
+ && appToken.allAppWindows.size() == 1 && !inAnimation;
+ if (!lastWinStartingNotAnimating && (win.mExiting || inAnimation)) {
// The exit animation is running... wait for it!
- //Slog.i(TAG, "*** Running exit animation...");
win.mExiting = true;
win.mRemoveOnExit = true;
final DisplayContent displayContent = win.getDisplayContent();
@@ -2757,7 +2759,6 @@
if (focusChanged) {
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
- //dump();
Binder.restoreCallingIdentity(origId);
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e9023fd..b42c8eb 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -251,11 +251,21 @@
&& mAppAnimator.animation == AppWindowAnimator.sDummyAnimation;
}
- /** Is this window currently animating? */
+ /** Is this window currently set to animate or currently animating?
+ * NOTE: The method will return true for cases where the window isn't currently animating, but
+ * is set to animate. i.e. if the window animation is currently set to a dummy placeholder
+ * animation. Use {@link #isWindowAnimatingNow} to know if the window is currently running a
+ * real animation. */
boolean isWindowAnimating() {
return mAnimation != null;
}
+ /** Is the window performing a real animation and not a dummy which is only waiting for an
+ * an animation to start? */
+ boolean isWindowAnimatingNow() {
+ return isWindowAnimating() && !isDummyAnimation();
+ }
+
void cancelExitAnimationForNextAnimationLocked() {
if (mAnimation != null) {
mAnimation.cancel();
diff --git a/services/net/java/android/net/dhcp/DhcpAckPacket.java b/services/net/java/android/net/dhcp/DhcpAckPacket.java
index c0e1d19..334f708 100644
--- a/services/net/java/android/net/dhcp/DhcpAckPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpAckPacket.java
@@ -30,8 +30,8 @@
private final Inet4Address mSrcIp;
DhcpAckPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
- Inet4Address clientIp, byte[] clientMac) {
- super(transId, secs, INADDR_ANY, clientIp, serverAddress, INADDR_ANY, clientMac, broadcast);
+ Inet4Address clientIp, Inet4Address yourIp, byte[] clientMac) {
+ super(transId, secs, clientIp, yourIp, serverAddress, INADDR_ANY, clientMac, broadcast);
mBroadcast = broadcast;
mSrcIp = serverAddress;
}
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 069b591..575a300 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -58,7 +58,7 @@
import java.util.Arrays;
import java.util.Random;
-import libcore.io.IoUtils;
+import libcore.io.IoBridge;
import static android.system.OsConstants.*;
import static android.net.dhcp.DhcpPacket.*;
@@ -297,9 +297,15 @@
return true;
}
+ private static void closeQuietly(FileDescriptor fd) {
+ try {
+ IoBridge.closeAndSignalBlockedThreads(fd);
+ } catch (IOException ignored) {}
+ }
+
private void closeSockets() {
- IoUtils.closeQuietly(mUdpSock);
- IoUtils.closeQuietly(mPacketSock);
+ closeQuietly(mUdpSock);
+ closeQuietly(mPacketSock);
}
private boolean setIpAddress(LinkAddress address) {
@@ -326,7 +332,7 @@
@Override
public void run() {
- maybeLog("Starting receive thread");
+ maybeLog("Receive thread started");
while (!stopped) {
try {
int length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
@@ -345,7 +351,7 @@
}
}
}
- maybeLog("Stopping receive thread");
+ maybeLog("Receive thread stopped");
}
}
@@ -463,6 +469,8 @@
return "CMD_KICK";
case CMD_RECEIVED_PACKET:
return "CMD_RECEIVED_PACKET";
+ case CMD_TIMEOUT:
+ return "CMD_TIMEOUT";
default:
return Integer.toString(what);
}
diff --git a/services/net/java/android/net/dhcp/DhcpOfferPacket.java b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
index af41708..7ca7100 100644
--- a/services/net/java/android/net/dhcp/DhcpOfferPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
@@ -32,8 +32,8 @@
* Generates a OFFER packet with the specified parameters.
*/
DhcpOfferPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
- Inet4Address clientIp, byte[] clientMac) {
- super(transId, secs, INADDR_ANY, clientIp, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
+ Inet4Address clientIp, Inet4Address yourIp, byte[] clientMac) {
+ super(transId, secs, clientIp, yourIp, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
mSrcIp = serverAddress;
}
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index 2a25d30..d42404b 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -919,7 +919,7 @@
break;
case DHCP_MESSAGE_TYPE_OFFER:
newPacket = new DhcpOfferPacket(
- transactionId, secs, broadcast, ipSrc, yourIp, clientMac);
+ transactionId, secs, broadcast, ipSrc, clientIp, yourIp, clientMac);
break;
case DHCP_MESSAGE_TYPE_REQUEST:
newPacket = new DhcpRequestPacket(
@@ -932,7 +932,7 @@
break;
case DHCP_MESSAGE_TYPE_ACK:
newPacket = new DhcpAckPacket(
- transactionId, secs, broadcast, ipSrc, yourIp, clientMac);
+ transactionId, secs, broadcast, ipSrc, clientIp, yourIp, clientMac);
break;
case DHCP_MESSAGE_TYPE_NAK:
newPacket = new DhcpNakPacket(
@@ -982,9 +982,9 @@
*/
public DhcpResults toDhcpResults() {
Inet4Address ipAddress = mYourIp;
- if (ipAddress == Inet4Address.ANY) {
+ if (ipAddress.equals(Inet4Address.ANY)) {
ipAddress = mClientIp;
- if (ipAddress == Inet4Address.ANY) {
+ if (ipAddress.equals(Inet4Address.ANY)) {
return null;
}
}
@@ -1052,7 +1052,7 @@
Inet4Address gateway, List<Inet4Address> dnsServers,
Inet4Address dhcpServerIdentifier, String domainName) {
DhcpPacket pkt = new DhcpOfferPacket(
- transactionId, (short) 0, broadcast, serverIpAddr, clientIpAddr, mac);
+ transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac);
pkt.mGateway = gateway;
pkt.mDnsServers = dnsServers;
pkt.mLeaseTime = timeout;
@@ -1072,7 +1072,7 @@
Inet4Address gateway, List<Inet4Address> dnsServers,
Inet4Address dhcpServerIdentifier, String domainName) {
DhcpPacket pkt = new DhcpAckPacket(
- transactionId, (short) 0, broadcast, serverIpAddr, clientIpAddr, mac);
+ transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac);
pkt.mGateway = gateway;
pkt.mDnsServers = dnsServers;
pkt.mLeaseTime = timeout;
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 636dd4d..919293a 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -36,6 +36,9 @@
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
+ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index f116042..e0e3fcf 100644
--- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -18,6 +18,7 @@
import android.net.NetworkUtils;
import android.net.DhcpResults;
+import android.net.LinkAddress;
import android.system.OsConstants;
import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;
@@ -34,19 +35,27 @@
(Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.1");
private static Inet4Address CLIENT_ADDR =
(Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.234");
+ // Use our own empty address instead of Inet4Address.ANY or INADDR_ANY to ensure that the code
+ // doesn't use == instead of equals when comparing addresses.
+ private static Inet4Address ANY = (Inet4Address) NetworkUtils.numericToInetAddress("0.0.0.0");
+
private static byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
class TestDhcpPacket extends DhcpPacket {
private byte mType;
// TODO: Make this a map of option numbers to bytes instead.
- private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes;
+ private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes, mNetmaskBytes;
- public TestDhcpPacket(byte type) {
- super(0xdeadbeef, (short) 0, INADDR_ANY, CLIENT_ADDR, INADDR_ANY, INADDR_ANY,
+ public TestDhcpPacket(byte type, Inet4Address clientIp, Inet4Address yourIp) {
+ super(0xdeadbeef, (short) 0, clientIp, yourIp, INADDR_ANY, INADDR_ANY,
CLIENT_MAC, true);
mType = type;
}
+ public TestDhcpPacket(byte type) {
+ this(type, INADDR_ANY, CLIENT_ADDR);
+ }
+
public TestDhcpPacket setDomainBytes(byte[] domainBytes) {
mDomainBytes = domainBytes;
return this;
@@ -62,6 +71,11 @@
return this;
}
+ public TestDhcpPacket setNetmaskBytes(byte[] netmaskBytes) {
+ mNetmaskBytes = netmaskBytes;
+ return this;
+ }
+
public ByteBuffer buildPacket(int encap, short unusedDestUdp, short unusedSrcUdp) {
ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
fillInPacket(encap, CLIENT_ADDR, SERVER_ADDR,
@@ -80,6 +94,9 @@
if (mLeaseTimeBytes != null) {
addTlv(buffer, DHCP_LEASE_TIME, mLeaseTimeBytes);
}
+ if (mNetmaskBytes != null) {
+ addTlv(buffer, DHCP_SUBNET_MASK, mNetmaskBytes);
+ }
addTlvEnd(buffer);
}
@@ -175,4 +192,50 @@
assertLeaseTimeParses(true, -2147483647, 2147483649L * 1000, maxIntPlusOneLease);
assertLeaseTimeParses(true, DhcpPacket.INFINITE_LEASE, 0, infiniteLease);
}
+
+ private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp,
+ byte[] netmaskBytes) {
+ checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes);
+ checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes);
+ }
+
+ private void checkIpAddress(String expected, byte type,
+ Inet4Address clientIp, Inet4Address yourIp,
+ byte[] netmaskBytes) {
+ ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp)
+ .setNetmaskBytes(netmaskBytes)
+ .build();
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
+ DhcpResults results = offerPacket.toDhcpResults();
+
+ if (expected != null) {
+ LinkAddress expectedAddress = new LinkAddress(expected);
+ assertEquals(expectedAddress, results.ipAddress);
+ } else {
+ assertNull(results);
+ }
+ }
+
+ @SmallTest
+ public void testIpAddress() throws Exception {
+ byte[] slash11Netmask = new byte[] { (byte) 0xff, (byte) 0xe0, 0x00, 0x00 };
+ byte[] slash24Netmask = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00 };
+ byte[] invalidNetmask = new byte[] { (byte) 0xff, (byte) 0xfb, (byte) 0xff, 0x00 };
+ Inet4Address example1 = (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.1");
+ Inet4Address example2 = (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.43");
+
+ // A packet without any addresses is not valid.
+ checkIpAddress(null, ANY, ANY, slash24Netmask);
+
+ // ClientIP is used iff YourIP is not present.
+ checkIpAddress("192.0.2.1/24", example2, example1, slash24Netmask);
+ checkIpAddress("192.0.2.43/11", example2, ANY, slash11Netmask);
+ checkIpAddress("192.0.2.43/11", ANY, example2, slash11Netmask);
+
+ // Invalid netmasks are ignored.
+ checkIpAddress(null, example2, ANY, invalidNetmask);
+
+ // If there is no netmask, implicit netmasks are used.
+ checkIpAddress("192.0.2.43/24", ANY, example2, null);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 48d8ffb..56f1d48c 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -20,6 +20,10 @@
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.getNetworkTypeName;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
@@ -30,21 +34,36 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkMisc;
+import android.net.NetworkRequest;
import android.net.RouteInfo;
+import android.os.ConditionVariable;
import android.os.Handler;
+import android.os.Looper;
import android.os.INetworkManagementService;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import android.util.LogPrinter;
+import com.android.server.connectivity.NetworkMonitor;
+
import org.mockito.ArgumentCaptor;
import java.net.InetAddress;
@@ -52,8 +71,10 @@
/**
* Tests for {@link ConnectivityService}.
+ *
+ * Build, install and run with:
+ * runtest frameworks-services -c com.android.server.ConnectivityServiceTest
*/
-@LargeTest
public class ConnectivityServiceTest extends AndroidTestCase {
private static final String TAG = "ConnectivityServiceTest";
@@ -75,73 +96,336 @@
private INetworkManagementService mNetManager;
private INetworkStatsService mStatsService;
private INetworkPolicyManager mPolicyService;
-// private ConnectivityService.NetworkFactory mNetFactory;
private BroadcastInterceptingContext mServiceContext;
private ConnectivityService mService;
+ private ConnectivityManager mCm;
+ private MockNetworkAgent mWiFiNetworkAgent;
+ private MockNetworkAgent mCellNetworkAgent;
-// TODO: rework with network factory
-// private MockNetwork mMobile;
-// private MockNetwork mWifi;
-//
-// private Handler mTrackerHandler;
-//
-// private static class MockNetwork {
-// public NetworkStateTracker tracker;
-// public NetworkInfo info;
-// public LinkProperties link;
-//
-// public MockNetwork(int type) {
-// tracker = mock(NetworkStateTracker.class);
-// info = new NetworkInfo(type, -1, getNetworkTypeName(type), null);
-// link = new LinkProperties();
-// }
-//
-// public void doReturnDefaults() {
-// // TODO: eventually CS should make defensive copies
-// doReturn(new NetworkInfo(info)).when(tracker).getNetworkInfo();
-// doReturn(new LinkProperties(link)).when(tracker).getLinkProperties();
-//
-// // fallback to default TCP buffers
-// doReturn("").when(tracker).getTcpBufferSizesPropName();
-// }
-// }
-//
-// @Override
-// public void setUp() throws Exception {
-// super.setUp();
-//
-// mServiceContext = new BroadcastInterceptingContext(getContext());
-//
-// mNetManager = mock(INetworkManagementService.class);
-// mStatsService = mock(INetworkStatsService.class);
-// mPolicyService = mock(INetworkPolicyManager.class);
-// mNetFactory = mock(ConnectivityService.NetworkFactory.class);
-//
-// mMobile = new MockNetwork(TYPE_MOBILE);
-// mWifi = new MockNetwork(TYPE_WIFI);
-//
-// // omit most network trackers
-// doThrow(new IllegalArgumentException("Not supported in test environment"))
-// .when(mNetFactory).createTracker(anyInt(), isA(NetworkConfig.class));
-//
-// doReturn(mMobile.tracker)
-// .when(mNetFactory).createTracker(eq(TYPE_MOBILE), isA(NetworkConfig.class));
-// doReturn(mWifi.tracker)
-// .when(mNetFactory).createTracker(eq(TYPE_WIFI), isA(NetworkConfig.class));
-//
-// final ArgumentCaptor<Handler> trackerHandler = ArgumentCaptor.forClass(Handler.class);
-// doNothing().when(mMobile.tracker)
-// .startMonitoring(isA(Context.class), trackerHandler.capture());
-//
-// mService = new ConnectivityService(
-// mServiceContext, mNetManager, mStatsService, mPolicyService);
-// mService.systemReady();
-//
-// mTrackerHandler = trackerHandler.getValue();
-// mTrackerHandler.getLooper().setMessageLogging(new LogPrinter(Log.INFO, TAG));
-// }
-//
+ private class MockContext extends BroadcastInterceptingContext {
+ MockContext(Context base) {
+ super(base);
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ // PendingIntents sent by the AlarmManager are not intercepted by
+ // BroadcastInterceptingContext so we must really register the receiver.
+ // This shouldn't effect the real NetworkMonitors as the action contains a random token.
+ if (filter.getAction(0).startsWith("android.net.netmon.lingerExpired")) {
+ return getBaseContext().registerReceiver(receiver, filter);
+ } else {
+ return super.registerReceiver(receiver, filter);
+ }
+ }
+
+ @Override
+ public Object getSystemService (String name) {
+ if (name == Context.CONNECTIVITY_SERVICE) return mCm;
+ return super.getSystemService(name);
+ }
+ }
+
+ private class MockNetworkAgent {
+ private final NetworkInfo mNetworkInfo;
+ private final NetworkCapabilities mNetworkCapabilities;
+ private final Thread mThread;
+ private NetworkAgent mNetworkAgent;
+
+ MockNetworkAgent(int transport) {
+ final int type = transportToLegacyType(transport);
+ final String typeName = ConnectivityManager.getNetworkTypeName(type);
+ mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
+ mNetworkCapabilities = new NetworkCapabilities();
+ mNetworkCapabilities.addTransportType(transport);
+ final int score;
+ switch (transport) {
+ case TRANSPORT_WIFI:
+ score = 60;
+ break;
+ case TRANSPORT_CELLULAR:
+ score = 50;
+ break;
+ default:
+ throw new UnsupportedOperationException("unimplemented network type");
+ }
+ final ConditionVariable initComplete = new ConditionVariable();
+ mThread = new Thread() {
+ public void run() {
+ Looper.prepare();
+ mNetworkAgent = new NetworkAgent(Looper.myLooper(), mServiceContext,
+ "Mock" + typeName, mNetworkInfo, mNetworkCapabilities,
+ new LinkProperties(), score, new NetworkMisc()) {
+ public void unwanted() {}
+ };
+ initComplete.open();
+ Looper.loop();
+ }
+ };
+ mThread.start();
+ initComplete.block();
+ }
+
+ /**
+ * Transition this NetworkAgent to CONNECTED state.
+ * @param validated Indicate if network should pretend to be validated.
+ */
+ public void connect(boolean validated) {
+ assertEquals(mNetworkInfo.getDetailedState(), DetailedState.IDLE);
+ assertFalse(mNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET));
+
+ // To pretend network is validated, we transition it to the CONNECTED state without
+ // NET_CAPABILITY_INTERNET so NetworkMonitor doesn't bother trying to validate and
+ // just rubber stamps it as validated. Afterwards we add NET_CAPABILITY_INTERNET so
+ // the network can satisfy the default request.
+ NetworkCallback callback = null;
+ final ConditionVariable validatedCv = new ConditionVariable();
+ if (validated) {
+ // If we connect a network without INTERNET capability, it'll get reaped.
+ // Prevent the reaping by adding a NetworkRequest.
+ NetworkRequest request = new NetworkRequest.Builder()
+ .addTransportType(mNetworkCapabilities.getTransportTypes()[0])
+ .build();
+ callback = new NetworkCallback() {
+ public void onCapabilitiesChanged(Network network,
+ NetworkCapabilities networkCapabilities) {
+ if (networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
+ validatedCv.open();
+ }
+ }
+ };
+ mCm.requestNetwork(request, callback);
+ } else {
+ mNetworkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
+ mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+
+ if (validated) {
+ // Wait for network to validate.
+ validatedCv.block();
+ mNetworkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
+ if (callback != null) mCm.unregisterNetworkCallback(callback);
+ }
+
+ public void disconnect() {
+ mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ }
+
+ public Network getNetwork() {
+ return new Network(mNetworkAgent.netId);
+ }
+ }
+
+ private class WrappedConnectivityService extends ConnectivityService {
+ public WrappedConnectivityService(Context context, INetworkManagementService netManager,
+ INetworkStatsService statsService, INetworkPolicyManager policyManager) {
+ super(context, netManager, statsService, policyManager);
+ }
+
+ @Override
+ protected int getDefaultTcpRwnd() {
+ // Prevent wrapped ConnectivityService from trying to write to SystemProperties.
+ return 0;
+ }
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mServiceContext = new MockContext(getContext());
+
+ mNetManager = mock(INetworkManagementService.class);
+ mStatsService = mock(INetworkStatsService.class);
+ mPolicyService = mock(INetworkPolicyManager.class);
+
+ mService = new WrappedConnectivityService(
+ mServiceContext, mNetManager, mStatsService, mPolicyService);
+ mService.systemReady();
+ mCm = new ConnectivityManager(mService);
+ }
+
+ private int transportToLegacyType(int transport) {
+ switch (transport) {
+ case TRANSPORT_WIFI:
+ return TYPE_WIFI;
+ case TRANSPORT_CELLULAR:
+ return TYPE_MOBILE;
+ default:
+ throw new IllegalStateException("Unknown transport" + transport);
+ }
+ }
+
+ private void verifyActiveNetwork(int transport) {
+ // Test getActiveNetworkInfo()
+ assertNotNull(mCm.getActiveNetworkInfo());
+ assertEquals(transportToLegacyType(transport), mCm.getActiveNetworkInfo().getType());
+ // Test getActiveNetwork()
+ assertNotNull(mCm.getActiveNetwork());
+ switch (transport) {
+ case TRANSPORT_WIFI:
+ assertEquals(mCm.getActiveNetwork(), mWiFiNetworkAgent.getNetwork());
+ break;
+ case TRANSPORT_CELLULAR:
+ assertEquals(mCm.getActiveNetwork(), mCellNetworkAgent.getNetwork());
+ break;
+ default:
+ throw new IllegalStateException("Unknown transport" + transport);
+ }
+ // Test getNetworkInfo(Network)
+ assertNotNull(mCm.getNetworkInfo(mCm.getActiveNetwork()));
+ assertEquals(transportToLegacyType(transport), mCm.getNetworkInfo(mCm.getActiveNetwork()).getType());
+ // Test getNetworkCapabilities(Network)
+ assertNotNull(mCm.getNetworkCapabilities(mCm.getActiveNetwork()));
+ assertTrue(mCm.getNetworkCapabilities(mCm.getActiveNetwork()).hasTransport(transport));
+ }
+
+ private void verifyNoNetwork() {
+ // Test getActiveNetworkInfo()
+ assertNull(mCm.getActiveNetworkInfo());
+ // Test getActiveNetwork()
+ assertNull(mCm.getActiveNetwork());
+ // Test getAllNetworks()
+ assertEquals(0, mCm.getAllNetworks().length);
+ }
+
+ /**
+ * Return a ConditionVariable that opens when {@code count} numbers of CONNECTIVITY_ACTION
+ * broadcasts are received.
+ */
+ private ConditionVariable waitForConnectivityBroadcasts(final int count) {
+ final ConditionVariable cv = new ConditionVariable();
+ mServiceContext.registerReceiver(new BroadcastReceiver() {
+ private int remaining = count;
+ public void onReceive(Context context, Intent intent) {
+ if (--remaining == 0) {
+ cv.open();
+ mServiceContext.unregisterReceiver(this);
+ }
+ }
+ }, new IntentFilter(CONNECTIVITY_ACTION));
+ return cv;
+ }
+
+ @LargeTest
+ public void testLingering() throws Exception {
+ // Decrease linger timeout to the minimum allowed by AlarmManagerService.
+ NetworkMonitor.SetDefaultLingerTime(5000);
+ verifyNoNetwork();
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ assertNull(mCm.getActiveNetworkInfo());
+ assertNull(mCm.getActiveNetwork());
+ // Test bringing up validated cellular.
+ ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mCellNetworkAgent.connect(true);
+ cv.block();
+ verifyActiveNetwork(TRANSPORT_CELLULAR);
+ assertEquals(2, mCm.getAllNetworks().length);
+ assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) ||
+ mCm.getAllNetworks()[1].equals(mCm.getActiveNetwork()));
+ assertTrue(mCm.getAllNetworks()[0].equals(mWiFiNetworkAgent.getNetwork()) ||
+ mCm.getAllNetworks()[1].equals(mWiFiNetworkAgent.getNetwork()));
+ // Test bringing up validated WiFi.
+ cv = waitForConnectivityBroadcasts(2);
+ mWiFiNetworkAgent.connect(true);
+ cv.block();
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ assertEquals(2, mCm.getAllNetworks().length);
+ assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) ||
+ mCm.getAllNetworks()[1].equals(mCm.getActiveNetwork()));
+ assertTrue(mCm.getAllNetworks()[0].equals(mCellNetworkAgent.getNetwork()) ||
+ mCm.getAllNetworks()[1].equals(mCellNetworkAgent.getNetwork()));
+ // Test cellular linger timeout.
+ try {
+ Thread.sleep(6000);
+ } catch (Exception e) {
+ }
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ assertEquals(1, mCm.getAllNetworks().length);
+ assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork());
+ // Test WiFi disconnect.
+ cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent.disconnect();
+ cv.block();
+ verifyNoNetwork();
+ }
+
+ @LargeTest
+ public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
+ // Test bringing up unvalidated WiFi
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent.connect(false);
+ cv.block();
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ // Test bringing up unvalidated cellular
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(false);
+ try {
+ Thread.sleep(1000);
+ } catch (Exception e) {
+ }
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ // Test cellular disconnect.
+ mCellNetworkAgent.disconnect();
+ try {
+ Thread.sleep(1000);
+ } catch (Exception e) {
+ }
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ // Test bringing up validated cellular
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ cv = waitForConnectivityBroadcasts(2);
+ mCellNetworkAgent.connect(true);
+ cv.block();
+ verifyActiveNetwork(TRANSPORT_CELLULAR);
+ // Test cellular disconnect.
+ cv = waitForConnectivityBroadcasts(2);
+ mCellNetworkAgent.disconnect();
+ cv.block();
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ // Test WiFi disconnect.
+ cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent.disconnect();
+ cv.block();
+ verifyNoNetwork();
+ }
+
+ @LargeTest
+ public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception {
+ // Test bringing up unvalidated cellular.
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mCellNetworkAgent.connect(false);
+ cv.block();
+ verifyActiveNetwork(TRANSPORT_CELLULAR);
+ // Test bringing up unvalidated WiFi.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ cv = waitForConnectivityBroadcasts(2);
+ mWiFiNetworkAgent.connect(false);
+ cv.block();
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ // Test WiFi disconnect.
+ cv = waitForConnectivityBroadcasts(2);
+ mWiFiNetworkAgent.disconnect();
+ cv.block();
+ verifyActiveNetwork(TRANSPORT_CELLULAR);
+ // Test cellular disconnect.
+ cv = waitForConnectivityBroadcasts(1);
+ mCellNetworkAgent.disconnect();
+ cv.block();
+ verifyNoNetwork();
+ }
+
// @Override
// public void tearDown() throws Exception {
// super.tearDown();
@@ -157,9 +441,9 @@
// mMobile.link.addRoute(MOBILE_ROUTE_V6);
// mMobile.doReturnDefaults();
//
-// nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION);
+// cv = waitForConnectivityBroadcasts(1);
// mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
-// nextConnBroadcast.get();
+// cv.block();
//
// // verify that both routes were added
// int mobileNetId = mMobile.tracker.getNetwork().netId;
@@ -177,9 +461,9 @@
// mMobile.link.addRoute(MOBILE_ROUTE_V6);
// mMobile.doReturnDefaults();
//
-// nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION);
+// cv = waitForConnectivityBroadcasts(1);
// mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
-// nextConnBroadcast.get();
+// cv.block();
//
// reset(mNetManager);
//
@@ -193,9 +477,9 @@
// // expect that mobile will be torn down
// doReturn(true).when(mMobile.tracker).teardown();
//
-// nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION);
+// cv = waitForConnectivityBroadcasts(1);
// mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mWifi.info).sendToTarget();
-// nextConnBroadcast.get();
+// cv.block();
//
// // verify that wifi routes added, and teardown requested
// int wifiNetId = mWifi.tracker.getNetwork().netId;
@@ -212,9 +496,9 @@
// mMobile.link.clear();
// mMobile.doReturnDefaults();
//
-// nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION);
+// cv = waitForConnectivityBroadcasts(1);
// mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
-// nextConnBroadcast.get();
+// cv.block();
//
// verify(mNetManager).removeRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V4));
// verify(mNetManager).removeRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V6));
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 6de887b..d8569bc 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -201,7 +201,7 @@
Slog.w(TAG, "finish does not match active session");
return;
}
- mActiveSession.cancel();
+ mActiveSession.cancelLocked();
mActiveSession = null;
}
@@ -251,7 +251,7 @@
// If there is an active session, cancel it to allow it to clean up its window and other
// state.
if (mActiveSession != null) {
- mActiveSession.cancel();
+ mActiveSession.cancelLocked();
mActiveSession = null;
}
try {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index b4629f2..0b430ca0 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -19,9 +19,9 @@
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
-import android.app.AssistContent;
-import android.app.AssistStructure;
import android.app.IActivityManager;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -376,6 +376,40 @@
return false;
}
+ public void cancelLocked() {
+ hideLocked();
+ mCanceled = true;
+ if (mBound) {
+ if (mSession != null) {
+ try {
+ mSession.destroy();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Voice interation session already dead");
+ }
+ }
+ if (mSession != null) {
+ try {
+ mAm.finishVoiceTask(mSession);
+ } catch (RemoteException e) {
+ }
+ }
+ mContext.unbindService(this);
+ try {
+ mIWindowManager.removeWindowToken(mToken);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed removing window token", e);
+ }
+ mBound = false;
+ mService = null;
+ mSession = null;
+ mInteractor = null;
+ }
+ if (mFullyBound) {
+ mContext.unbindService(mFullConnection);
+ mFullyBound = false;
+ }
+ }
+
public boolean deliverNewSessionLocked(IVoiceInteractionSession session,
IVoiceInteractor interactor) {
mSession = session;
@@ -432,39 +466,6 @@
mService = null;
}
- public void cancel() {
- mCanceled = true;
- if (mBound) {
- if (mSession != null) {
- try {
- mSession.destroy();
- } catch (RemoteException e) {
- Slog.w(TAG, "Voice interation session already dead");
- }
- }
- if (mSession != null) {
- try {
- mAm.finishVoiceTask(mSession);
- } catch (RemoteException e) {
- }
- }
- mContext.unbindService(this);
- try {
- mIWindowManager.removeWindowToken(mToken);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed removing window token", e);
- }
- mBound = false;
- mService = null;
- mSession = null;
- mInteractor = null;
- }
- if (mFullyBound) {
- mContext.unbindService(mFullConnection);
- mFullyBound = false;
- }
- }
-
private boolean isStructureEnabled() {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, mUser) != 0;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index dfb02bb..353bb5f 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -229,6 +229,14 @@
*/
public static final String STRING_VVM_TYPE = "string_vvm_type";
+ /**
+ * The package name of the carrier's visual voicemail app to ensure that dialer visual voicemail
+ * and carrier visual voicemail are not active at the same time.
+ *
+ * @hide
+ */
+ public static final String STRING_CARRIER_VVM_PACKAGE_NAME = "string_carrier_vvm_package_name";
+
// These variables are used by the MMS service and exposed through another API, {@link
// SmsManager}. The variable names and string values are copied from there.
public static final String BOOL_MMS_ALIAS_ENABLED = "aliasEnabled";
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f51cb65..2445bf6e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2298,7 +2298,8 @@
}
/**
- * Returns the voice mail count. Return 0 if unavailable.
+ * Returns the voice mail count. Return 0 if unavailable, -1 if there are unread voice messages
+ * but the count is unknown.
* <p>
* Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index f66a9ce..dae1ac3 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -17,9 +17,9 @@
package com.android.test.voiceinteraction;
import android.app.ActivityManager;
+import android.app.VoiceInteractor;
import android.app.AssistContent;
import android.app.AssistStructure;
-import android.app.VoiceInteractor;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -71,7 +71,7 @@
public void onCreate(Bundle args, int startFlags) {
super.onCreate(args, startFlags);
ActivityManager am = getContext().getSystemService(ActivityManager.class);
- am.setWatchHeapLimit(40*1024*1024);
+ am.setWatchHeapLimit(40 * 1024 * 1024);
}
@Override
@@ -118,6 +118,30 @@
return mContentView;
}
+ public void onHandleAssist(Bundle assistBundle) {
+ if (assistBundle != null) {
+ Bundle assistContext = assistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
+ if (assistContext != null) {
+ mAssistStructure = AssistStructure.getAssistStructure(assistContext);
+ if (mAssistStructure != null) {
+ if (mAssistVisualizer != null) {
+ mAssistVisualizer.setAssistStructure(mAssistStructure);
+ }
+ }
+ AssistContent content = AssistContent.getAssistContent(assistContext);
+ if (content != null) {
+ Log.i(TAG, "Assist intent: " + content.getIntent());
+ Log.i(TAG, "Assist clipdata: " + content.getClipData());
+ }
+ return;
+ }
+ }
+ if (mAssistVisualizer != null) {
+ mAssistVisualizer.clearAssistData();
+ }
+ }
+
+ /*
@Override
public void onHandleAssist(Bundle data, AssistStructure structure, AssistContent content) {
mAssistStructure = structure;
@@ -131,6 +155,7 @@
Log.i(TAG, "Assist clipdata: " + content.getClipData());
}
}
+ */
@Override
public void onHandleScreenshot(Bitmap screenshot) {
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 23d679d..8c128d3 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -27,7 +27,6 @@
sources := \
BigBuffer.cpp \
BinaryResourceParser.cpp \
- BinaryXmlPullParser.cpp \
BindingXmlPullParser.cpp \
ConfigDescription.cpp \
Debug.cpp \
@@ -53,6 +52,7 @@
ScopedXmlPullParser.cpp \
SourceXmlPullParser.cpp \
XliffXmlPullParser.cpp \
+ XmlDom.cpp \
XmlFlattener.cpp \
ZipEntry.cpp \
ZipFile.cpp
@@ -76,6 +76,7 @@
StringPool_test.cpp \
Util_test.cpp \
XliffXmlPullParser_test.cpp \
+ XmlDom_test.cpp \
XmlFlattener_test.cpp
cIncludes := \
diff --git a/tools/aapt2/BinaryXmlPullParser.cpp b/tools/aapt2/BinaryXmlPullParser.cpp
deleted file mode 100644
index 476a215..0000000
--- a/tools/aapt2/BinaryXmlPullParser.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "BinaryXmlPullParser.h"
-#include "Maybe.h"
-#include "Util.h"
-
-#include <androidfw/ResourceTypes.h>
-#include <memory>
-#include <string>
-#include <vector>
-
-namespace aapt {
-
-static XmlPullParser::Event codeToEvent(android::ResXMLParser::event_code_t code) {
- switch (code) {
- case android::ResXMLParser::START_DOCUMENT:
- return XmlPullParser::Event::kStartDocument;
- case android::ResXMLParser::END_DOCUMENT:
- return XmlPullParser::Event::kEndDocument;
- case android::ResXMLParser::START_NAMESPACE:
- return XmlPullParser::Event::kStartNamespace;
- case android::ResXMLParser::END_NAMESPACE:
- return XmlPullParser::Event::kEndNamespace;
- case android::ResXMLParser::START_TAG:
- return XmlPullParser::Event::kStartElement;
- case android::ResXMLParser::END_TAG:
- return XmlPullParser::Event::kEndElement;
- case android::ResXMLParser::TEXT:
- return XmlPullParser::Event::kText;
- default:
- break;
- }
- return XmlPullParser::Event::kBadDocument;
-}
-
-BinaryXmlPullParser::BinaryXmlPullParser(const std::shared_ptr<android::ResXMLTree>& parser)
- : mParser(parser), mEvent(Event::kStartDocument), mHasComment(false), sEmpty(), sEmpty8(),
- mDepth(0) {
-}
-
-XmlPullParser::Event BinaryXmlPullParser::next() {
- mStr1.clear();
- mStr2.clear();
- mAttributes.clear();
-
- android::ResXMLParser::event_code_t code;
- if (mHasComment) {
- mHasComment = false;
- code = mParser->getEventType();
- } else {
- code = mParser->next();
- if (code != android::ResXMLParser::BAD_DOCUMENT) {
- size_t len;
- const char16_t* comment = mParser->getComment(&len);
- if (comment) {
- mHasComment = true;
- mStr1.assign(comment, len);
- return XmlPullParser::Event::kComment;
- }
- }
- }
-
- size_t len;
- const char16_t* data;
- mEvent = codeToEvent(code);
- switch (mEvent) {
- case Event::kStartNamespace:
- case Event::kEndNamespace: {
- data = mParser->getNamespacePrefix(&len);
- if (data) {
- mStr1.assign(data, len);
- } else {
- mStr1.clear();
- }
- data = mParser->getNamespaceUri(&len);
- if (data) {
- mStr2.assign(data, len);
- } else {
- mStr2.clear();
- }
-
- Maybe<std::u16string> result = util::extractPackageFromNamespace(mStr2);
- if (result) {
- if (mEvent == Event::kStartNamespace) {
- mPackageAliases.emplace_back(mStr1, result.value());
- } else {
- assert(mPackageAliases.back().second == result.value());
- mPackageAliases.pop_back();
- }
- }
- break;
- }
-
- case Event::kStartElement:
- copyAttributes();
- // fallthrough
-
- case Event::kEndElement:
- data = mParser->getElementNamespace(&len);
- if (data) {
- mStr1.assign(data, len);
- } else {
- mStr1.clear();
- }
- data = mParser->getElementName(&len);
- if (data) {
- mStr2.assign(data, len);
- } else {
- mStr2.clear();
- }
- break;
-
- case Event::kText:
- data = mParser->getText(&len);
- if (data) {
- mStr1.assign(data, len);
- } else {
- mStr1.clear();
- }
- break;
-
- default:
- break;
- }
- return mEvent;
-}
-
-XmlPullParser::Event BinaryXmlPullParser::getEvent() const {
- if (mHasComment) {
- return XmlPullParser::Event::kComment;
- }
- return mEvent;
-}
-
-const std::string& BinaryXmlPullParser::getLastError() const {
- return sEmpty8;
-}
-
-const std::u16string& BinaryXmlPullParser::getComment() const {
- if (mHasComment) {
- return mStr1;
- }
- return sEmpty;
-}
-
-size_t BinaryXmlPullParser::getLineNumber() const {
- return mParser->getLineNumber();
-}
-
-size_t BinaryXmlPullParser::getDepth() const {
- return mDepth;
-}
-
-const std::u16string& BinaryXmlPullParser::getText() const {
- if (!mHasComment && mEvent == XmlPullParser::Event::kText) {
- return mStr1;
- }
- return sEmpty;
-}
-
-const std::u16string& BinaryXmlPullParser::getNamespacePrefix() const {
- if (!mHasComment && (mEvent == XmlPullParser::Event::kStartNamespace ||
- mEvent == XmlPullParser::Event::kEndNamespace)) {
- return mStr1;
- }
- return sEmpty;
-}
-
-const std::u16string& BinaryXmlPullParser::getNamespaceUri() const {
- if (!mHasComment && (mEvent == XmlPullParser::Event::kStartNamespace ||
- mEvent == XmlPullParser::Event::kEndNamespace)) {
- return mStr2;
- }
- return sEmpty;
-}
-
-bool BinaryXmlPullParser::applyPackageAlias(std::u16string* package,
- const std::u16string& defaultPackage) const {
- const auto endIter = mPackageAliases.rend();
- for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) {
- if (iter->first == *package) {
- if (iter->second.empty()) {
- *package = defaultPackage;
- } else {
- *package = iter->second;
- }
- return true;
- }
- }
- return false;
-}
-
-const std::u16string& BinaryXmlPullParser::getElementNamespace() const {
- if (!mHasComment && (mEvent == XmlPullParser::Event::kStartElement ||
- mEvent == XmlPullParser::Event::kEndElement)) {
- return mStr1;
- }
- return sEmpty;
-}
-
-const std::u16string& BinaryXmlPullParser::getElementName() const {
- if (!mHasComment && (mEvent == XmlPullParser::Event::kStartElement ||
- mEvent == XmlPullParser::Event::kEndElement)) {
- return mStr2;
- }
- return sEmpty;
-}
-
-size_t BinaryXmlPullParser::getAttributeCount() const {
- return mAttributes.size();
-}
-
-XmlPullParser::const_iterator BinaryXmlPullParser::beginAttributes() const {
- return mAttributes.begin();
-}
-
-XmlPullParser::const_iterator BinaryXmlPullParser::endAttributes() const {
- return mAttributes.end();
-}
-
-void BinaryXmlPullParser::copyAttributes() {
- const size_t attrCount = mParser->getAttributeCount();
- if (attrCount > 0) {
- mAttributes.reserve(attrCount);
- for (size_t i = 0; i < attrCount; i++) {
- XmlPullParser::Attribute attr;
- size_t len;
- const char16_t* str = mParser->getAttributeNamespace(i, &len);
- if (str) {
- attr.namespaceUri.assign(str, len);
- }
- str = mParser->getAttributeName(i, &len);
- if (str) {
- attr.name.assign(str, len);
- }
- str = mParser->getAttributeStringValue(i, &len);
- if (str) {
- attr.value.assign(str, len);
- }
- mAttributes.push_back(std::move(attr));
- }
- }
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/BinaryXmlPullParser.h b/tools/aapt2/BinaryXmlPullParser.h
deleted file mode 100644
index 16fc8b7..0000000
--- a/tools/aapt2/BinaryXmlPullParser.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef AAPT_BINARY_XML_PULL_PARSER_H
-#define AAPT_BINARY_XML_PULL_PARSER_H
-
-#include "XmlPullParser.h"
-
-#include <androidfw/ResourceTypes.h>
-#include <memory>
-#include <string>
-#include <vector>
-
-namespace aapt {
-
-/**
- * Wraps a ResTable into the canonical XmlPullParser interface.
- */
-class BinaryXmlPullParser : public XmlPullParser {
-public:
- BinaryXmlPullParser(const std::shared_ptr<android::ResXMLTree>& parser);
- BinaryXmlPullParser(const BinaryXmlPullParser& rhs) = delete;
-
- Event getEvent() const override;
- const std::string& getLastError() const override;
- Event next() override;
-
- const std::u16string& getComment() const override;
- size_t getLineNumber() const override;
- size_t getDepth() const override;
-
- const std::u16string& getText() const override;
-
- const std::u16string& getNamespacePrefix() const override;
- const std::u16string& getNamespaceUri() const override;
- bool applyPackageAlias(std::u16string* package, const std::u16string& defaultpackage)
- const override;
-
- const std::u16string& getElementNamespace() const override;
- const std::u16string& getElementName() const override;
-
- const_iterator beginAttributes() const override;
- const_iterator endAttributes() const override;
- size_t getAttributeCount() const override;
-
-private:
- void copyAttributes();
-
- std::shared_ptr<android::ResXMLTree> mParser;
- std::u16string mStr1;
- std::u16string mStr2;
- std::vector<Attribute> mAttributes;
- Event mEvent;
- bool mHasComment;
- const std::u16string sEmpty;
- const std::string sEmpty8;
- size_t mDepth;
- std::vector<std::pair<std::u16string, std::u16string>> mPackageAliases;
-};
-
-} // namespace aapt
-
-#endif // AAPT_BINARY_XML_PULL_PARSER_H
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 91639c5..84957b4 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -17,7 +17,6 @@
#include "AppInfo.h"
#include "BigBuffer.h"
#include "BinaryResourceParser.h"
-#include "BinaryXmlPullParser.h"
#include "BindingXmlPullParser.h"
#include "Debug.h"
#include "Files.h"
@@ -128,7 +127,7 @@
auto iter = style.entries.begin();
while (iter != style.entries.end()) {
if (iter->key.name.package == u"android") {
- size_t sdkLevel = findAttributeSdkLevel(iter->key.name.entry);
+ size_t sdkLevel = findAttributeSdkLevel(iter->key.name);
if (sdkLevel > 1 && sdkLevel > configValue.config.sdkVersion) {
// Record that we are about to strip this.
stripped.emplace_back(std::move(*iter));
@@ -300,6 +299,42 @@
ResourceName dumpStyleTarget;
};
+struct IdCollector : public xml::Visitor {
+ IdCollector(const Source& source, const std::shared_ptr<ResourceTable>& table) :
+ mSource(source), mTable(table) {
+ }
+
+ virtual void visit(xml::Text* node) override {}
+
+ virtual void visit(xml::Namespace* node) override {
+ for (const auto& child : node->children) {
+ child->accept(this);
+ }
+ }
+
+ virtual void visit(xml::Element* node) override {
+ for (const xml::Attribute& attr : node->attributes) {
+ bool create = false;
+ bool priv = false;
+ ResourceNameRef nameRef;
+ if (ResourceParser::tryParseReference(attr.value, &nameRef, &create, &priv)) {
+ if (create) {
+ mTable->addResource(nameRef, {}, mSource.line(node->lineNumber),
+ util::make_unique<Id>());
+ }
+ }
+ }
+
+ for (const auto& child : node->children) {
+ child->accept(this);
+ }
+ }
+
+private:
+ Source mSource;
+ std::shared_ptr<ResourceTable> mTable;
+};
+
bool compileXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
const CompileItem& item, ZipFile* outApk) {
std::ifstream in(item.source.path, std::ifstream::binary);
@@ -308,20 +343,19 @@
return false;
}
+ SourceLogger logger(item.source);
+ std::unique_ptr<xml::Node> root = xml::inflate(&in, &logger);
+ if (!root) {
+ return false;
+ }
+
+ // Collect any resource ID's declared here.
+ IdCollector idCollector(item.source, table);
+ root->accept(&idCollector);
+
BigBuffer outBuffer(1024);
-
- // No resolver, since we are not compiling attributes here.
- XmlFlattener flattener(table, {});
-
- XmlFlattener::Options xmlOptions;
- xmlOptions.defaultPackage = table->getPackage();
- xmlOptions.keepRawValues = true;
-
- std::shared_ptr<XmlPullParser> parser = std::make_shared<SourceXmlPullParser>(in);
-
- Maybe<size_t> minStrippedSdk = flattener.flatten(item.source, parser, &outBuffer,
- xmlOptions);
- if (!minStrippedSdk) {
+ if (!xml::flatten(root.get(), options.appInfo.package, &outBuffer)) {
+ logger.error() << "failed to encode XML." << std::endl;
return false;
}
@@ -369,19 +403,13 @@
bool linkXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
const std::shared_ptr<IResolver>& resolver, const LinkItem& item,
const void* data, size_t dataLen, ZipFile* outApk, std::queue<LinkItem>* outQueue) {
- std::shared_ptr<android::ResXMLTree> tree = std::make_shared<android::ResXMLTree>();
- if (tree->setTo(data, dataLen, false) != android::NO_ERROR) {
+ SourceLogger logger(item.source);
+ std::unique_ptr<xml::Node> root = xml::inflate(data, dataLen, &logger);
+ if (!root) {
return false;
}
- std::shared_ptr<XmlPullParser> parser = std::make_shared<BinaryXmlPullParser>(tree);
-
- BigBuffer outBuffer(1024);
- XmlFlattener flattener({}, resolver);
-
- XmlFlattener::Options xmlOptions;
- xmlOptions.defaultPackage = item.originalPackage;
-
+ xml::FlattenOptions xmlOptions;
if (options.packageType == AaptOptions::PackageType::StaticLibrary) {
xmlOptions.keepRawValues = true;
}
@@ -392,16 +420,12 @@
xmlOptions.maxSdkAttribute = item.config.sdkVersion ? item.config.sdkVersion : 1;
}
- std::shared_ptr<BindingXmlPullParser> binding;
- if (item.name.type == ResourceType::kLayout) {
- // Layouts may have defined bindings, so we need to make sure they get processed.
- binding = std::make_shared<BindingXmlPullParser>(parser);
- parser = binding;
- }
-
- Maybe<size_t> minStrippedSdk = flattener.flatten(item.source, parser, &outBuffer,
- xmlOptions);
+ BigBuffer outBuffer(1024);
+ Maybe<size_t> minStrippedSdk = xml::flattenAndLink(item.source, root.get(),
+ item.originalPackage, resolver,
+ xmlOptions, &outBuffer);
if (!minStrippedSdk) {
+ logger.error() << "failed to encode XML." << std::endl;
return false;
}
@@ -431,30 +455,6 @@
<< buildFileReference(item) << "' to apk." << std::endl;
return false;
}
-
- if (binding && !options.bindingOutput.path.empty()) {
- // We generated a binding xml file, write it out.
- Source bindingOutput = options.bindingOutput;
- appendPath(&bindingOutput.path, buildFileReference(item));
-
- if (!mkdirs(bindingOutput.path)) {
- Logger::error(bindingOutput) << strerror(errno) << std::endl;
- return false;
- }
-
- appendPath(&bindingOutput.path, "bind.xml");
-
- std::ofstream bout(bindingOutput.path);
- if (!bout) {
- Logger::error(bindingOutput) << strerror(errno) << std::endl;
- return false;
- }
-
- if (!binding->writeToFile(bout)) {
- Logger::error(bindingOutput) << strerror(errno) << std::endl;
- return false;
- }
- }
return true;
}
@@ -504,13 +504,15 @@
return false;
}
- BigBuffer outBuffer(1024);
- std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(in);
- XmlFlattener flattener({}, resolver);
+ SourceLogger logger(options.manifest);
+ std::unique_ptr<xml::Node> root = xml::inflate(&in, &logger);
+ if (!root) {
+ return false;
+ }
- XmlFlattener::Options xmlOptions;
- xmlOptions.defaultPackage = options.appInfo.package;
- if (!flattener.flatten(options.manifest, xmlParser, &outBuffer, xmlOptions)) {
+ BigBuffer outBuffer(1024);
+ if (!xml::flattenAndLink(options.manifest, root.get(), options.appInfo.package, resolver, {},
+ &outBuffer)) {
return false;
}
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 3f156a6..9bdae49 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -14,11 +14,51 @@
* limitations under the License.
*/
+#include "SdkConstants.h"
+
+#include <algorithm>
#include <string>
#include <unordered_map>
+#include <vector>
namespace aapt {
+static const std::vector<std::pair<uint16_t, size_t>> sAttrIdMap = {
+ { 0x021c, 1 },
+ { 0x021d, 2 },
+ { 0x0269, SDK_CUPCAKE },
+ { 0x028d, SDK_DONUT },
+ { 0x02ad, SDK_ECLAIR },
+ { 0x02b3, SDK_ECLAIR_0_1 },
+ { 0x02b5, SDK_ECLAIR_MR1 },
+ { 0x02bd, SDK_FROYO },
+ { 0x02cb, SDK_GINGERBREAD },
+ { 0x0361, SDK_HONEYCOMB },
+ { 0x0366, SDK_HONEYCOMB_MR1 },
+ { 0x03a6, SDK_HONEYCOMB_MR2 },
+ { 0x03ae, SDK_JELLY_BEAN },
+ { 0x03cc, SDK_JELLY_BEAN_MR1 },
+ { 0x03da, SDK_JELLY_BEAN_MR2 },
+ { 0x03f1, SDK_KITKAT },
+ { 0x03f6, SDK_KITKAT_WATCH },
+ { 0x04ce, SDK_LOLLIPOP },
+};
+
+static bool lessEntryId(const std::pair<uint16_t, size_t>& p, uint16_t entryId) {
+ return p.first < entryId;
+}
+
+size_t findAttributeSdkLevel(ResourceId id) {
+ if (id.packageId() != 0x01 && id.typeId() != 0x01) {
+ return 0;
+ }
+ auto iter = std::lower_bound(sAttrIdMap.begin(), sAttrIdMap.end(), id.entryId(), lessEntryId);
+ if (iter == sAttrIdMap.end()) {
+ return SDK_LOLLIPOP_MR1;
+ }
+ return iter->second;
+}
+
static const std::unordered_map<std::u16string, size_t> sAttrMap = {
{ u"marqueeRepeatLimit", 2 },
{ u"windowNoDisplay", 3 },
@@ -682,12 +722,16 @@
{ u"colorEdgeEffect", 21 }
};
-size_t findAttributeSdkLevel(const std::u16string& name) {
- auto iter = sAttrMap.find(name);
+size_t findAttributeSdkLevel(const ResourceName& name) {
+ if (name.package != u"android" && name.type != ResourceType::kAttr) {
+ return 0;
+ }
+
+ auto iter = sAttrMap.find(name.entry);
if (iter != sAttrMap.end()) {
return iter->second;
}
- return 0;
+ return SDK_LOLLIPOP_MR1;
}
} // namespace aapt
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 469c355..803da03 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -19,8 +19,6 @@
#include "Resource.h"
-#include <string>
-
namespace aapt {
enum {
@@ -46,7 +44,8 @@
SDK_LOLLIPOP_MR1 = 22,
};
-size_t findAttributeSdkLevel(const std::u16string& name);
+size_t findAttributeSdkLevel(ResourceId id);
+size_t findAttributeSdkLevel(const ResourceName& name);
} // namespace aapt
diff --git a/tools/aapt2/XmlDom.cpp b/tools/aapt2/XmlDom.cpp
new file mode 100644
index 0000000..763029f
--- /dev/null
+++ b/tools/aapt2/XmlDom.cpp
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Logger.h"
+#include "Util.h"
+#include "XmlDom.h"
+#include "XmlPullParser.h"
+
+#include <cassert>
+#include <memory>
+#include <stack>
+#include <string>
+#include <tuple>
+
+namespace aapt {
+namespace xml {
+
+constexpr char kXmlNamespaceSep = 1;
+
+struct Stack {
+ std::unique_ptr<xml::Node> root;
+ std::stack<xml::Node*> nodeStack;
+ std::u16string pendingComment;
+};
+
+/**
+ * Extracts the namespace and name of an expanded element or attribute name.
+ */
+static void splitName(const char* name, std::u16string* outNs, std::u16string* outName) {
+ const char* p = name;
+ while (*p != 0 && *p != kXmlNamespaceSep) {
+ p++;
+ }
+
+ if (*p == 0) {
+ outNs->clear();
+ *outName = util::utf8ToUtf16(name);
+ } else {
+ *outNs = util::utf8ToUtf16(StringPiece(name, (p - name)));
+ *outName = util::utf8ToUtf16(p + 1);
+ }
+}
+
+static void addToStack(Stack* stack, XML_Parser parser, std::unique_ptr<Node> node) {
+ node->lineNumber = XML_GetCurrentLineNumber(parser);
+ node->columnNumber = XML_GetCurrentColumnNumber(parser);
+
+ Node* thisNode = node.get();
+ if (!stack->nodeStack.empty()) {
+ stack->nodeStack.top()->addChild(std::move(node));
+ } else {
+ stack->root = std::move(node);
+ }
+
+ if (thisNode->type != NodeType::kText) {
+ stack->nodeStack.push(thisNode);
+ }
+}
+
+static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri) {
+ XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+ Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+ std::unique_ptr<Namespace> ns = util::make_unique<Namespace>();
+ if (prefix) {
+ ns->namespacePrefix = util::utf8ToUtf16(prefix);
+ }
+
+ if (uri) {
+ ns->namespaceUri = util::utf8ToUtf16(uri);
+ }
+
+ addToStack(stack, parser, std::move(ns));
+}
+
+static void XMLCALL endNamespaceHandler(void* userData, const char* prefix) {
+ XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+ Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+ assert(!stack->nodeStack.empty());
+ stack->nodeStack.pop();
+}
+
+static bool lessAttribute(const Attribute& lhs, const Attribute& rhs) {
+ return std::tie(lhs.namespaceUri, lhs.name, lhs.value) <
+ std::tie(rhs.namespaceUri, rhs.name, rhs.value);
+}
+
+static void XMLCALL startElementHandler(void* userData, const char* name, const char** attrs) {
+ XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+ Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+ std::unique_ptr<Element> el = util::make_unique<Element>();
+ splitName(name, &el->namespaceUri, &el->name);
+
+ while (*attrs) {
+ Attribute attribute;
+ splitName(*attrs++, &attribute.namespaceUri, &attribute.name);
+ attribute.value = util::utf8ToUtf16(*attrs++);
+
+ // Insert in sorted order.
+ auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(), attribute,
+ lessAttribute);
+ el->attributes.insert(iter, std::move(attribute));
+ }
+
+ el->comment = std::move(stack->pendingComment);
+ addToStack(stack, parser, std::move(el));
+}
+
+static void XMLCALL endElementHandler(void* userData, const char* name) {
+ XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+ Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+ assert(!stack->nodeStack.empty());
+ stack->nodeStack.top()->comment = std::move(stack->pendingComment);
+ stack->nodeStack.pop();
+}
+
+static void XMLCALL characterDataHandler(void* userData, const char* s, int len) {
+ XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+ Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+ if (!s || len <= 0) {
+ return;
+ }
+
+ // See if we can just append the text to a previous text node.
+ if (!stack->nodeStack.empty()) {
+ Node* currentParent = stack->nodeStack.top();
+ if (!currentParent->children.empty()) {
+ Node* lastChild = currentParent->children.back().get();
+ if (lastChild->type == NodeType::kText) {
+ Text* text = static_cast<Text*>(lastChild);
+ text->text += util::utf8ToUtf16(StringPiece(s, len));
+ return;
+ }
+ }
+ }
+
+ std::unique_ptr<Text> text = util::make_unique<Text>();
+ text->text = util::utf8ToUtf16(StringPiece(s, len));
+ addToStack(stack, parser, std::move(text));
+}
+
+static void XMLCALL commentDataHandler(void* userData, const char* comment) {
+ XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+ Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+ if (!stack->pendingComment.empty()) {
+ stack->pendingComment += '\n';
+ }
+ stack->pendingComment += util::utf8ToUtf16(comment);
+}
+
+std::unique_ptr<Node> inflate(std::istream* in, SourceLogger* logger) {
+ Stack stack;
+
+ XML_Parser parser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
+ XML_SetUserData(parser, &stack);
+ XML_UseParserAsHandlerArg(parser);
+ XML_SetElementHandler(parser, startElementHandler, endElementHandler);
+ XML_SetNamespaceDeclHandler(parser, startNamespaceHandler, endNamespaceHandler);
+ XML_SetCharacterDataHandler(parser, characterDataHandler);
+ XML_SetCommentHandler(parser, commentDataHandler);
+
+ char buffer[1024];
+ while (!in->eof()) {
+ in->read(buffer, sizeof(buffer) / sizeof(buffer[0]));
+ if (in->bad() && !in->eof()) {
+ stack.root = {};
+ logger->error() << strerror(errno) << std::endl;
+ break;
+ }
+
+ if (XML_Parse(parser, buffer, in->gcount(), in->eof()) == XML_STATUS_ERROR) {
+ stack.root = {};
+ logger->error(XML_GetCurrentLineNumber(parser))
+ << XML_ErrorString(XML_GetErrorCode(parser)) << std::endl;
+ break;
+ }
+ }
+
+ XML_ParserFree(parser);
+ return std::move(stack.root);
+}
+
+static void copyAttributes(Element* el, android::ResXMLParser* parser) {
+ const size_t attrCount = parser->getAttributeCount();
+ if (attrCount > 0) {
+ el->attributes.reserve(attrCount);
+ for (size_t i = 0; i < attrCount; i++) {
+ Attribute attr;
+ size_t len;
+ const char16_t* str16 = parser->getAttributeNamespace(i, &len);
+ if (str16) {
+ attr.namespaceUri.assign(str16, len);
+ }
+
+ str16 = parser->getAttributeName(i, &len);
+ if (str16) {
+ attr.name.assign(str16, len);
+ }
+
+ str16 = parser->getAttributeStringValue(i, &len);
+ if (str16) {
+ attr.value.assign(str16, len);
+ }
+ el->attributes.push_back(std::move(attr));
+ }
+ }
+}
+
+std::unique_ptr<Node> inflate(const void* data, size_t dataLen, SourceLogger* logger) {
+ std::unique_ptr<Node> root;
+ std::stack<Node*> nodeStack;
+
+ android::ResXMLTree tree;
+ if (tree.setTo(data, dataLen) != android::NO_ERROR) {
+ return {};
+ }
+
+ android::ResXMLParser::event_code_t code;
+ while ((code = tree.next()) != android::ResXMLParser::BAD_DOCUMENT &&
+ code != android::ResXMLParser::END_DOCUMENT) {
+ std::unique_ptr<Node> newNode;
+ switch (code) {
+ case android::ResXMLParser::START_NAMESPACE: {
+ std::unique_ptr<Namespace> node = util::make_unique<Namespace>();
+ size_t len;
+ const char16_t* str16 = tree.getNamespacePrefix(&len);
+ if (str16) {
+ node->namespacePrefix.assign(str16, len);
+ }
+
+ str16 = tree.getNamespaceUri(&len);
+ if (str16) {
+ node->namespaceUri.assign(str16, len);
+ }
+ newNode = std::move(node);
+ break;
+ }
+
+ case android::ResXMLParser::START_TAG: {
+ std::unique_ptr<Element> node = util::make_unique<Element>();
+ size_t len;
+ const char16_t* str16 = tree.getElementNamespace(&len);
+ if (str16) {
+ node->namespaceUri.assign(str16, len);
+ }
+
+ str16 = tree.getElementName(&len);
+ if (str16) {
+ node->name.assign(str16, len);
+ }
+
+ copyAttributes(node.get(), &tree);
+
+ newNode = std::move(node);
+ break;
+ }
+
+ case android::ResXMLParser::TEXT: {
+ std::unique_ptr<Text> node = util::make_unique<Text>();
+ size_t len;
+ const char16_t* str16 = tree.getText(&len);
+ if (str16) {
+ node->text.assign(str16, len);
+ }
+ newNode = std::move(node);
+ break;
+ }
+
+ case android::ResXMLParser::END_NAMESPACE:
+ case android::ResXMLParser::END_TAG:
+ assert(!nodeStack.empty());
+ nodeStack.pop();
+ break;
+
+ default:
+ assert(false);
+ break;
+ }
+
+ if (newNode) {
+ newNode->lineNumber = tree.getLineNumber();
+
+ Node* thisNode = newNode.get();
+ if (!root) {
+ assert(nodeStack.empty());
+ root = std::move(newNode);
+ } else {
+ assert(!nodeStack.empty());
+ nodeStack.top()->addChild(std::move(newNode));
+ }
+
+ if (thisNode->type != NodeType::kText) {
+ nodeStack.push(thisNode);
+ }
+ }
+ }
+ return std::move(root);
+}
+
+Node::Node(NodeType type) : type(type), parent(nullptr), lineNumber(0), columnNumber(0) {
+}
+
+void Node::addChild(std::unique_ptr<Node> child) {
+ child->parent = this;
+ children.push_back(std::move(child));
+}
+
+Namespace::Namespace() : BaseNode(NodeType::kNamespace) {
+}
+
+std::unique_ptr<Node> Namespace::clone() const {
+ Namespace* ns = new Namespace();
+ ns->lineNumber = lineNumber;
+ ns->columnNumber = columnNumber;
+ ns->comment = comment;
+ ns->namespacePrefix = namespacePrefix;
+ ns->namespaceUri = namespaceUri;
+ for (auto& child : children) {
+ ns->addChild(child->clone());
+ }
+ return std::unique_ptr<Node>(ns);
+}
+
+Element::Element() : BaseNode(NodeType::kElement) {
+}
+
+std::unique_ptr<Node> Element::clone() const {
+ Element* el = new Element();
+ el->lineNumber = lineNumber;
+ el->columnNumber = columnNumber;
+ el->comment = comment;
+ el->namespaceUri = namespaceUri;
+ el->name = name;
+ el->attributes = attributes;
+ for (auto& child : children) {
+ el->addChild(child->clone());
+ }
+ return std::unique_ptr<Node>(el);
+}
+
+Attribute* Element::findAttribute(const StringPiece16& ns, const StringPiece16& name) {
+ for (auto& attr : attributes) {
+ if (ns == attr.namespaceUri && name == attr.name) {
+ return &attr;
+ }
+ }
+ return nullptr;
+}
+
+Element* Element::findChild(const StringPiece16& ns, const StringPiece16& name) {
+ return findChildWithAttribute(ns, name, nullptr);
+}
+
+Element* Element::findChildWithAttribute(const StringPiece16& ns, const StringPiece16& name,
+ const Attribute* reqAttr) {
+ for (auto& childNode : children) {
+ Node* child = childNode.get();
+ while (child->type == NodeType::kNamespace) {
+ if (child->children.empty()) {
+ break;
+ }
+ child = child->children[0].get();
+ }
+
+ if (child->type == NodeType::kElement) {
+ Element* el = static_cast<Element*>(child);
+ if (ns == el->namespaceUri && name == el->name) {
+ if (!reqAttr) {
+ return el;
+ }
+
+ Attribute* attrName = el->findAttribute(reqAttr->namespaceUri, reqAttr->name);
+ if (attrName && attrName->value == reqAttr->value) {
+ return el;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+std::vector<Element*> Element::getChildElements() {
+ std::vector<Element*> elements;
+ for (auto& childNode : children) {
+ Node* child = childNode.get();
+ while (child->type == NodeType::kNamespace) {
+ if (child->children.empty()) {
+ break;
+ }
+ child = child->children[0].get();
+ }
+
+ if (child->type == NodeType::kElement) {
+ elements.push_back(static_cast<Element*>(child));
+ }
+ }
+ return elements;
+}
+
+Text::Text() : BaseNode(NodeType::kText) {
+}
+
+std::unique_ptr<Node> Text::clone() const {
+ Text* el = new Text();
+ el->lineNumber = lineNumber;
+ el->columnNumber = columnNumber;
+ el->comment = comment;
+ el->text = text;
+ return std::unique_ptr<Node>(el);
+}
+
+} // namespace xml
+} // namespace aapt
diff --git a/tools/aapt2/XmlDom.h b/tools/aapt2/XmlDom.h
new file mode 100644
index 0000000..6931884
--- /dev/null
+++ b/tools/aapt2/XmlDom.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_XML_DOM_H
+#define AAPT_XML_DOM_H
+
+#include "Logger.h"
+#include "StringPiece.h"
+
+#include <istream>
+#include <libexpat/expat.h>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace aapt {
+namespace xml {
+
+struct Visitor;
+
+/**
+ * The type of node. Can be used to downcast to the concrete XML node
+ * class.
+ */
+enum class NodeType {
+ kNamespace,
+ kElement,
+ kText,
+};
+
+/**
+ * Base class for all XML nodes.
+ */
+struct Node {
+ NodeType type;
+ Node* parent;
+ size_t lineNumber;
+ size_t columnNumber;
+ std::u16string comment;
+ std::vector<std::unique_ptr<Node>> children;
+
+ Node(NodeType type);
+ void addChild(std::unique_ptr<Node> child);
+ virtual std::unique_ptr<Node> clone() const = 0;
+ virtual void accept(Visitor* visitor) = 0;
+ virtual ~Node() {}
+};
+
+/**
+ * Base class that implements the visitor methods for a
+ * subclass of Node.
+ */
+template <typename Derived>
+struct BaseNode : public Node {
+ BaseNode(NodeType t);
+ virtual void accept(Visitor* visitor) override;
+};
+
+/**
+ * A Namespace XML node. Can only have one child.
+ */
+struct Namespace : public BaseNode<Namespace> {
+ std::u16string namespacePrefix;
+ std::u16string namespaceUri;
+
+ Namespace();
+ virtual std::unique_ptr<Node> clone() const override;
+};
+
+/**
+ * An XML attribute.
+ */
+struct Attribute {
+ std::u16string namespaceUri;
+ std::u16string name;
+ std::u16string value;
+};
+
+/**
+ * An Element XML node.
+ */
+struct Element : public BaseNode<Element> {
+ std::u16string namespaceUri;
+ std::u16string name;
+ std::vector<Attribute> attributes;
+
+ Element();
+ virtual std::unique_ptr<Node> clone() const override;
+ Attribute* findAttribute(const StringPiece16& ns, const StringPiece16& name);
+ xml::Element* findChild(const StringPiece16& ns, const StringPiece16& name);
+ xml::Element* findChildWithAttribute(const StringPiece16& ns, const StringPiece16& name,
+ const xml::Attribute* reqAttr);
+ std::vector<xml::Element*> getChildElements();
+};
+
+/**
+ * A Text (CDATA) XML node. Can not have any children.
+ */
+struct Text : public BaseNode<Text> {
+ std::u16string text;
+
+ Text();
+ virtual std::unique_ptr<Node> clone() const override;
+};
+
+/**
+ * Inflates an XML DOM from a text stream, logging errors to the logger.
+ * Returns the root node on success, or nullptr on failure.
+ */
+std::unique_ptr<Node> inflate(std::istream* in, SourceLogger* logger);
+
+/**
+ * Inflates an XML DOM from a binary ResXMLTree, logging errors to the logger.
+ * Returns the root node on success, or nullptr on failure.
+ */
+std::unique_ptr<Node> inflate(const void* data, size_t dataLen, SourceLogger* logger);
+
+/**
+ * A visitor interface for the different XML Node subtypes.
+ */
+struct Visitor {
+ virtual void visit(Namespace* node) = 0;
+ virtual void visit(Element* node) = 0;
+ virtual void visit(Text* text) = 0;
+};
+
+// Implementations
+
+template <typename Derived>
+BaseNode<Derived>::BaseNode(NodeType type) : Node(type) {
+}
+
+template <typename Derived>
+void BaseNode<Derived>::accept(Visitor* visitor) {
+ visitor->visit(static_cast<Derived*>(this));
+}
+
+} // namespace xml
+} // namespace aapt
+
+#endif // AAPT_XML_DOM_H
diff --git a/tools/aapt2/XmlDom_test.cpp b/tools/aapt2/XmlDom_test.cpp
new file mode 100644
index 0000000..0217144
--- /dev/null
+++ b/tools/aapt2/XmlDom_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "XmlDom.h"
+
+#include <gtest/gtest.h>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+
+TEST(XmlDomTest, Inflate) {
+ std::stringstream in(kXmlPreamble);
+ in << R"EOF(
+ <Layout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView android:id="@+id/id"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </Layout>
+ )EOF";
+
+ SourceLogger logger(Source{ "/test/path" });
+ std::unique_ptr<xml::Node> root = xml::inflate(&in, &logger);
+ ASSERT_NE(root, nullptr);
+
+ EXPECT_EQ(root->type, xml::NodeType::kNamespace);
+ xml::Namespace* ns = static_cast<xml::Namespace*>(root.get());
+ EXPECT_EQ(ns->namespaceUri, u"http://schemas.android.com/apk/res/android");
+ EXPECT_EQ(ns->namespacePrefix, u"android");
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/XmlFlattener.cpp b/tools/aapt2/XmlFlattener.cpp
index f78e38d..56b5613d 100644
--- a/tools/aapt2/XmlFlattener.cpp
+++ b/tools/aapt2/XmlFlattener.cpp
@@ -34,425 +34,444 @@
#include <vector>
namespace aapt {
+namespace xml {
-constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
+constexpr uint32_t kLowPriority = 0xffffffffu;
-struct AttributeValueFlattener : ValueVisitor {
- AttributeValueFlattener(
- std::shared_ptr<IResolver> resolver, SourceLogger* logger,
- android::Res_value* outValue, std::shared_ptr<XmlPullParser> parser, bool* outError,
- StringPool::Ref rawValue, std::u16string* defaultPackage,
- std::vector<std::pair<StringPool::Ref, android::ResStringPool_ref*>>* outStringRefs) :
- mResolver(resolver), mLogger(logger), mOutValue(outValue), mParser(parser),
- mError(outError), mRawValue(rawValue), mDefaultPackage(defaultPackage),
- mStringRefs(outStringRefs) {
+// A vector that maps String refs to their final destination in the out buffer.
+using FlatStringRefList = std::vector<std::pair<StringPool::Ref, android::ResStringPool_ref*>>;
+
+struct XmlFlattener : public Visitor {
+ XmlFlattener(BigBuffer* outBuffer, StringPool* pool, FlatStringRefList* stringRefs,
+ const std::u16string& defaultPackage) :
+ mOut(outBuffer), mPool(pool), mStringRefs(stringRefs),
+ mDefaultPackage(defaultPackage) {
}
- void visit(Reference& reference, ValueVisitorArgs&) override {
- // First see if we can convert the package name from a prefix to a real
- // package name.
- ResourceName aliasedName = reference.name;
+ // No copying.
+ XmlFlattener(const XmlFlattener&) = delete;
+ XmlFlattener& operator=(const XmlFlattener&) = delete;
- if (!reference.name.package.empty()) {
- // Only if we specified a package do we look for its alias.
- mParser->applyPackageAlias(&reference.name.package, *mDefaultPackage);
- } else {
- reference.name.package = *mDefaultPackage;
+ void writeNamespace(Namespace* node, uint16_t type) {
+ const size_t startIndex = mOut->size();
+ android::ResXMLTree_node* flatNode = mOut->nextBlock<android::ResXMLTree_node>();
+ android::ResXMLTree_namespaceExt* flatNs =
+ mOut->nextBlock<android::ResXMLTree_namespaceExt>();
+ mOut->align4();
+
+ flatNode->header = { type, sizeof(*flatNode), (uint32_t)(mOut->size() - startIndex) };
+ flatNode->lineNumber = node->lineNumber;
+ flatNode->comment.index = -1;
+ addString(node->namespacePrefix, kLowPriority, &flatNs->prefix);
+ addString(node->namespaceUri, kLowPriority, &flatNs->uri);
+ }
+
+ virtual void visit(Namespace* node) override {
+ // Extract the package/prefix from this namespace node.
+ Maybe<std::u16string> package = util::extractPackageFromNamespace(node->namespaceUri);
+ if (package) {
+ mPackageAliases.emplace_back(
+ node->namespacePrefix,
+ package.value().empty() ? mDefaultPackage : package.value());
}
- Maybe<ResourceId> result = mResolver->findId(reference.name);
- if (!result || !result.value().isValid()) {
- std::ostream& out = mLogger->error(mParser->getLineNumber())
- << "unresolved reference '"
- << aliasedName
- << "'";
- if (aliasedName != reference.name) {
- out << " (aka '" << reference.name << "')";
+ writeNamespace(node, android::RES_XML_START_NAMESPACE_TYPE);
+ for (const auto& child : node->children) {
+ child->accept(this);
+ }
+ writeNamespace(node, android::RES_XML_END_NAMESPACE_TYPE);
+
+ if (package) {
+ mPackageAliases.pop_back();
+ }
+ }
+
+ virtual void visit(Text* node) override {
+ if (util::trimWhitespace(node->text).empty()) {
+ return;
+ }
+
+ const size_t startIndex = mOut->size();
+ android::ResXMLTree_node* flatNode = mOut->nextBlock<android::ResXMLTree_node>();
+ android::ResXMLTree_cdataExt* flatText = mOut->nextBlock<android::ResXMLTree_cdataExt>();
+ mOut->align4();
+
+ const uint16_t type = android::RES_XML_CDATA_TYPE;
+ flatNode->header = { type, sizeof(*flatNode), (uint32_t)(mOut->size() - startIndex) };
+ flatNode->lineNumber = node->lineNumber;
+ flatNode->comment.index = -1;
+ addString(node->text, kLowPriority, &flatText->data);
+ }
+
+ virtual void visit(Element* node) override {
+ const size_t startIndex = mOut->size();
+ android::ResXMLTree_node* flatNode = mOut->nextBlock<android::ResXMLTree_node>();
+ android::ResXMLTree_attrExt* flatElem = mOut->nextBlock<android::ResXMLTree_attrExt>();
+
+ const uint16_t type = android::RES_XML_START_ELEMENT_TYPE;
+ flatNode->header = { type, sizeof(*flatNode), 0 };
+ flatNode->lineNumber = node->lineNumber;
+ flatNode->comment.index = -1;
+
+ addString(node->namespaceUri, kLowPriority, &flatElem->ns);
+ addString(node->name, kLowPriority, &flatElem->name);
+ flatElem->attributeStart = sizeof(*flatElem);
+ flatElem->attributeSize = sizeof(android::ResXMLTree_attribute);
+ flatElem->attributeCount = node->attributes.size();
+
+ if (!writeAttributes(mOut, node, flatElem)) {
+ mError = true;
+ }
+
+ mOut->align4();
+ flatNode->header.size = (uint32_t)(mOut->size() - startIndex);
+
+ for (const auto& child : node->children) {
+ child->accept(this);
+ }
+
+ const size_t startEndIndex = mOut->size();
+ android::ResXMLTree_node* flatEndNode = mOut->nextBlock<android::ResXMLTree_node>();
+ android::ResXMLTree_endElementExt* flatEndElem =
+ mOut->nextBlock<android::ResXMLTree_endElementExt>();
+ mOut->align4();
+
+ const uint16_t endType = android::RES_XML_END_ELEMENT_TYPE;
+ flatEndNode->header = { endType, sizeof(*flatEndNode),
+ (uint32_t)(mOut->size() - startEndIndex) };
+ flatEndNode->lineNumber = node->lineNumber;
+ flatEndNode->comment.index = -1;
+
+ addString(node->namespaceUri, kLowPriority, &flatEndElem->ns);
+ addString(node->name, kLowPriority, &flatEndElem->name);
+ }
+
+ bool success() const {
+ return !mError;
+ }
+
+protected:
+ void addString(const StringPiece16& str, uint32_t priority, android::ResStringPool_ref* dest) {
+ if (!str.empty()) {
+ mStringRefs->emplace_back(mPool->makeRef(str, StringPool::Context{ priority }), dest);
+ } else {
+ // The device doesn't think a string of size 0 is the same as null.
+ dest->index = -1;
+ }
+ }
+
+ void addString(const StringPool::Ref& ref, android::ResStringPool_ref* dest) {
+ mStringRefs->emplace_back(ref, dest);
+ }
+
+ Maybe<std::u16string> getPackageAlias(const std::u16string& prefix) {
+ const auto endIter = mPackageAliases.rend();
+ for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) {
+ if (iter->first == prefix) {
+ return iter->second;
}
- out << "'." << std::endl;
- *mError = true;
- } else {
- reference.id = result.value();
- reference.flatten(*mOutValue);
}
+ return {};
}
- void visit(String& string, ValueVisitorArgs&) override {
- mOutValue->dataType = android::Res_value::TYPE_STRING;
- mStringRefs->emplace_back(
- mRawValue,
- reinterpret_cast<android::ResStringPool_ref*>(mOutValue->data));
+ const std::u16string& getDefaultPackage() const {
+ return mDefaultPackage;
}
- void visitItem(Item& item, ValueVisitorArgs&) override {
- item.flatten(*mOutValue);
- }
+ /**
+ * Subclasses override this to deal with attributes. Attributes can be flattened as
+ * raw values or as resources.
+ */
+ virtual bool writeAttributes(BigBuffer* out, Element* node,
+ android::ResXMLTree_attrExt* flatElem) = 0;
private:
- std::shared_ptr<IResolver> mResolver;
- SourceLogger* mLogger;
- android::Res_value* mOutValue;
- std::shared_ptr<XmlPullParser> mParser;
- bool* mError;
- StringPool::Ref mRawValue;
- std::u16string* mDefaultPackage;
- std::vector<std::pair<StringPool::Ref, android::ResStringPool_ref*>>* mStringRefs;
+ BigBuffer* mOut;
+ StringPool* mPool;
+ FlatStringRefList* mStringRefs;
+ std::u16string mDefaultPackage;
+ bool mError = false;
+ std::vector<std::pair<std::u16string, std::u16string>> mPackageAliases;
};
-struct XmlAttribute {
- uint32_t resourceId;
- const XmlPullParser::Attribute* xmlAttr;
- const Attribute* attr;
- StringPool::Ref nameRef;
+/**
+ * Flattens XML, encoding the attributes as raw strings. This is used in the compile phase.
+ */
+struct CompileXmlFlattener : public XmlFlattener {
+ CompileXmlFlattener(BigBuffer* outBuffer, StringPool* pool, FlatStringRefList* stringRefs,
+ const std::u16string& defaultPackage) :
+ XmlFlattener(outBuffer, pool, stringRefs, defaultPackage) {
+ }
+
+ virtual bool writeAttributes(BigBuffer* out, Element* node,
+ android::ResXMLTree_attrExt* flatElem) override {
+ flatElem->attributeCount = node->attributes.size();
+ if (node->attributes.empty()) {
+ return true;
+ }
+
+ android::ResXMLTree_attribute* flatAttrs = out->nextBlock<android::ResXMLTree_attribute>(
+ node->attributes.size());
+ for (const Attribute& attr : node->attributes) {
+ addString(attr.namespaceUri, kLowPriority, &flatAttrs->ns);
+ addString(attr.name, kLowPriority, &flatAttrs->name);
+ addString(attr.value, kLowPriority, &flatAttrs->rawValue);
+ flatAttrs++;
+ }
+ return true;
+ }
};
-static bool lessAttributeId(const XmlAttribute& a, uint32_t id) {
+struct AttributeToFlatten {
+ uint32_t resourceId = 0;
+ const Attribute* xmlAttr = nullptr;
+ const ::aapt::Attribute* resourceAttr = nullptr;
+};
+
+static bool lessAttributeId(const AttributeToFlatten& a, uint32_t id) {
return a.resourceId < id;
}
-XmlFlattener::XmlFlattener(const std::shared_ptr<ResourceTable>& table,
- const std::shared_ptr<IResolver>& resolver) :
- mTable(table), mResolver(resolver) {
-}
+/**
+ * Flattens XML, encoding the attributes as resources.
+ */
+struct LinkedXmlFlattener : public XmlFlattener {
+ LinkedXmlFlattener(BigBuffer* outBuffer, StringPool* pool,
+ std::map<std::u16string, StringPool>* packagePools,
+ FlatStringRefList* stringRefs,
+ const std::u16string& defaultPackage,
+ const std::shared_ptr<IResolver>& resolver,
+ SourceLogger* logger,
+ const FlattenOptions& options) :
+ XmlFlattener(outBuffer, pool, stringRefs, defaultPackage), mResolver(resolver),
+ mLogger(logger), mPackagePools(packagePools), mOptions(options) {
+ }
+
+ virtual bool writeAttributes(BigBuffer* out, Element* node,
+ android::ResXMLTree_attrExt* flatElem) override {
+ bool error = false;
+ std::vector<AttributeToFlatten> sortedAttributes;
+ uint32_t nextAttributeId = 0x80000000u;
+
+ // Sort and filter attributes by their resource ID.
+ for (const Attribute& attr : node->attributes) {
+ AttributeToFlatten attrToFlatten;
+ attrToFlatten.xmlAttr = &attr;
+
+ Maybe<std::u16string> package = util::extractPackageFromNamespace(attr.namespaceUri);
+ if (package) {
+ // Find the Attribute object via our Resolver.
+ ResourceName attrName = { package.value(), ResourceType::kAttr, attr.name };
+ if (attrName.package.empty()) {
+ attrName.package = getDefaultPackage();
+ }
+
+ Maybe<IResolver::Entry> result = mResolver->findAttribute(attrName);
+ if (!result || !result.value().id.isValid() || !result.value().attr) {
+ error = true;
+ mLogger->error(node->lineNumber)
+ << "unresolved attribute '" << attrName << "'."
+ << std::endl;
+ } else {
+ attrToFlatten.resourceId = result.value().id.id;
+ attrToFlatten.resourceAttr = result.value().attr;
+
+ size_t sdk = findAttributeSdkLevel(attrToFlatten.resourceId);
+ if (mOptions.maxSdkAttribute && sdk > mOptions.maxSdkAttribute.value()) {
+ // We need to filter this attribute out.
+ mSmallestFilteredSdk = std::min(mSmallestFilteredSdk, sdk);
+ continue;
+ }
+ }
+ }
+
+ if (attrToFlatten.resourceId == 0) {
+ // Attributes that have no resource ID (because they don't belong to a
+ // package) should appear after those that do have resource IDs. Assign
+ // them some integer value that will appear after.
+ attrToFlatten.resourceId = nextAttributeId++;
+ }
+
+ // Insert the attribute into the sorted vector.
+ auto iter = std::lower_bound(sortedAttributes.begin(), sortedAttributes.end(),
+ attrToFlatten.resourceId, lessAttributeId);
+ sortedAttributes.insert(iter, std::move(attrToFlatten));
+ }
+
+ flatElem->attributeCount = sortedAttributes.size();
+ if (sortedAttributes.empty()) {
+ return true;
+ }
+
+ android::ResXMLTree_attribute* flatAttr = out->nextBlock<android::ResXMLTree_attribute>(
+ sortedAttributes.size());
+
+ // Now that we have sorted the attributes into their final encoded order, it's time
+ // to actually write them out.
+ uint16_t attributeIndex = 1;
+ for (const AttributeToFlatten& attrToFlatten : sortedAttributes) {
+ Maybe<std::u16string> package = util::extractPackageFromNamespace(
+ attrToFlatten.xmlAttr->namespaceUri);
+
+ // Assign the indices for specific attributes.
+ if (package && package.value() == u"android" && attrToFlatten.xmlAttr->name == u"id") {
+ flatElem->idIndex = attributeIndex;
+ } else if (attrToFlatten.xmlAttr->namespaceUri.empty()) {
+ if (attrToFlatten.xmlAttr->name == u"class") {
+ flatElem->classIndex = attributeIndex;
+ } else if (attrToFlatten.xmlAttr->name == u"style") {
+ flatElem->styleIndex = attributeIndex;
+ }
+ }
+ attributeIndex++;
+
+ // Add the namespaceUri and name to the list of StringRefs to encode.
+ addString(attrToFlatten.xmlAttr->namespaceUri, kLowPriority, &flatAttr->ns);
+ flatAttr->rawValue.index = -1;
+
+ if (!attrToFlatten.resourceAttr) {
+ addString(attrToFlatten.xmlAttr->name, kLowPriority, &flatAttr->name);
+ } else {
+ // We've already extracted the package successfully before.
+ assert(package);
+
+ // Attribute names are stored without packages, but we use
+ // their StringPool index to lookup their resource IDs.
+ // This will cause collisions, so we can't dedupe
+ // attribute names from different packages. We use separate
+ // pools that we later combine.
+ //
+ // Lookup the StringPool for this package and make the reference there.
+ StringPool::Ref nameRef = (*mPackagePools)[package.value()].makeRef(
+ attrToFlatten.xmlAttr->name,
+ StringPool::Context{ attrToFlatten.resourceId });
+
+ // Add it to the list of strings to flatten.
+ addString(nameRef, &flatAttr->name);
+
+ if (mOptions.keepRawValues) {
+ // Keep raw values (this is for static libraries).
+ // TODO(with a smarter inflater for binary XML, we can do without this).
+ addString(attrToFlatten.xmlAttr->value, kLowPriority, &flatAttr->rawValue);
+ }
+ }
+
+ error |= !flattenItem(node, attrToFlatten.xmlAttr->value, attrToFlatten.resourceAttr,
+ flatAttr);
+ flatAttr->typedValue.size = sizeof(flatAttr->typedValue);
+ flatAttr++;
+ }
+ return !error;
+ }
+
+ Maybe<size_t> getSmallestFilteredSdk() const {
+ if (mSmallestFilteredSdk == std::numeric_limits<size_t>::max()) {
+ return {};
+ }
+ return mSmallestFilteredSdk;
+ }
+
+private:
+ bool flattenItem(const Node* el, const std::u16string& value, const ::aapt::Attribute* attr,
+ android::ResXMLTree_attribute* flatAttr) {
+ std::unique_ptr<Item> item;
+ if (!attr) {
+ bool create = false;
+ item = ResourceParser::tryParseReference(value, &create);
+ if (!item) {
+ flatAttr->typedValue.dataType = android::Res_value::TYPE_STRING;
+ addString(value, kLowPriority, &flatAttr->rawValue);
+ addString(value, kLowPriority, reinterpret_cast<android::ResStringPool_ref*>(
+ &flatAttr->typedValue.data));
+ return true;
+ }
+ } else {
+ item = ResourceParser::parseItemForAttribute(value, *attr);
+ if (!item) {
+ if (!(attr->typeMask & android::ResTable_map::TYPE_STRING)) {
+ mLogger->error(el->lineNumber)
+ << "'"
+ << value
+ << "' is not compatible with attribute '"
+ << *attr
+ << "'."
+ << std::endl;
+ return false;
+ }
+
+ flatAttr->typedValue.dataType = android::Res_value::TYPE_STRING;
+ addString(value, kLowPriority, &flatAttr->rawValue);
+ addString(value, kLowPriority, reinterpret_cast<android::ResStringPool_ref*>(
+ &flatAttr->typedValue.data));
+ return true;
+ }
+ }
+
+ assert(item);
+
+ bool error = false;
+
+ // If this is a reference, resolve the name into an ID.
+ visitFunc<Reference>(*item, [&](Reference& reference) {
+ // First see if we can convert the package name from a prefix to a real
+ // package name.
+ ResourceName realName = reference.name;
+ if (!realName.package.empty()) {
+ Maybe<std::u16string> package = getPackageAlias(realName.package);
+ if (package) {
+ realName.package = package.value();
+ }
+ } else {
+ realName.package = getDefaultPackage();
+ }
+
+ Maybe<ResourceId> result = mResolver->findId(realName);
+ if (!result || !result.value().isValid()) {
+ std::ostream& out = mLogger->error(el->lineNumber)
+ << "unresolved reference '"
+ << reference.name
+ << "'";
+ if (realName != reference.name) {
+ out << " (aka '" << realName << "')";
+ }
+ out << "'." << std::endl;
+ error = true;
+ } else {
+ reference.id = result.value();
+ }
+ });
+
+ if (error) {
+ return false;
+ }
+
+ item->flatten(flatAttr->typedValue);
+ return true;
+ }
+
+ std::shared_ptr<IResolver> mResolver;
+ SourceLogger* mLogger;
+ std::map<std::u16string, StringPool>* mPackagePools;
+ FlattenOptions mOptions;
+ size_t mSmallestFilteredSdk = std::numeric_limits<size_t>::max();
+};
/**
- * Reads events from the parser and writes to a BigBuffer. The binary XML file
- * expects the StringPool to appear first, but we haven't collected the strings yet. We
- * write to a temporary BigBuffer while parsing the input, adding strings we encounter
- * to the StringPool. At the end, we write the StringPool to the given BigBuffer and
+ * The binary XML file expects the StringPool to appear first, but we haven't collected the
+ * strings yet. We write to a temporary BigBuffer while parsing the input, adding strings
+ * we encounter to the StringPool. At the end, we write the StringPool to the given BigBuffer and
* then move the data from the temporary BigBuffer into the given one. This incurs no
* copies as the given BigBuffer simply takes ownership of the data.
*/
-Maybe<size_t> XmlFlattener::flatten(const Source& source,
- const std::shared_ptr<XmlPullParser>& parser,
- BigBuffer* outBuffer, Options options) {
- SourceLogger logger(source);
- StringPool pool;
- bool error = false;
-
- size_t smallestStrippedAttributeSdk = std::numeric_limits<size_t>::max();
-
- // Attribute names are stored without packages, but we use
- // their StringPool index to lookup their resource IDs.
- // This will cause collisions, so we can't dedupe
- // attribute names from different packages. We use separate
- // pools that we later combine.
- std::map<std::u16string, StringPool> packagePools;
-
- // Attribute resource IDs are stored in the same order
- // as the attribute names appear in the StringPool.
- // Since the StringPool contains more than just attribute
- // names, to maintain a tight packing of resource IDs,
- // we must ensure that attribute names appear first
- // in our StringPool. For this, we assign a low priority
- // (0xffffffff) to non-attribute strings. Attribute
- // names will be stored along with a priority equal
- // to their resource ID so that they are ordered.
- StringPool::Context lowPriority { 0xffffffffu };
-
- // Once we sort the StringPool, we can assign the updated indices
- // to the correct data locations.
- std::vector<std::pair<StringPool::Ref, android::ResStringPool_ref*>> stringRefs;
-
- // Since we don't know the size of the final StringPool, we write to this
- // temporary BigBuffer, which we will append to outBuffer later.
- BigBuffer out(1024);
- while (XmlPullParser::isGoodEvent(parser->next())) {
- XmlPullParser::Event event = parser->getEvent();
- switch (event) {
- case XmlPullParser::Event::kStartNamespace:
- case XmlPullParser::Event::kEndNamespace: {
- const size_t startIndex = out.size();
- android::ResXMLTree_node* node = out.nextBlock<android::ResXMLTree_node>();
- if (event == XmlPullParser::Event::kStartNamespace) {
- node->header.type = android::RES_XML_START_NAMESPACE_TYPE;
- } else {
- node->header.type = android::RES_XML_END_NAMESPACE_TYPE;
- }
-
- node->header.headerSize = sizeof(*node);
- node->lineNumber = parser->getLineNumber();
- node->comment.index = -1;
-
- android::ResXMLTree_namespaceExt* ns =
- out.nextBlock<android::ResXMLTree_namespaceExt>();
- stringRefs.emplace_back(
- pool.makeRef(parser->getNamespacePrefix(), lowPriority), &ns->prefix);
- stringRefs.emplace_back(
- pool.makeRef(parser->getNamespaceUri(), lowPriority), &ns->uri);
-
- out.align4();
- node->header.size = out.size() - startIndex;
- break;
- }
-
- case XmlPullParser::Event::kStartElement: {
- const size_t startIndex = out.size();
- android::ResXMLTree_node* node = out.nextBlock<android::ResXMLTree_node>();
- node->header.type = android::RES_XML_START_ELEMENT_TYPE;
- node->header.headerSize = sizeof(*node);
- node->lineNumber = parser->getLineNumber();
- node->comment.index = -1;
-
- android::ResXMLTree_attrExt* elem = out.nextBlock<android::ResXMLTree_attrExt>();
- if (!parser->getElementNamespace().empty()) {
- stringRefs.emplace_back(
- pool.makeRef(parser->getElementNamespace(), lowPriority), &elem->ns);
- } else {
- // The device doesn't think a string of size 0 is the same as null.
- elem->ns.index = -1;
- }
- stringRefs.emplace_back(
- pool.makeRef(parser->getElementName(), lowPriority), &elem->name);
- elem->attributeStart = sizeof(*elem);
- elem->attributeSize = sizeof(android::ResXMLTree_attribute);
-
- // The resource system expects attributes to be sorted by resource ID.
- std::vector<XmlAttribute> sortedAttributes;
- uint32_t nextAttributeId = 0;
- const auto endAttrIter = parser->endAttributes();
- for (auto attrIter = parser->beginAttributes();
- attrIter != endAttrIter;
- ++attrIter) {
- uint32_t id;
- StringPool::Ref nameRef;
- const Attribute* attr = nullptr;
-
- if (options.maxSdkAttribute && attrIter->namespaceUri == kSchemaAndroid) {
- size_t sdkVersion = findAttributeSdkLevel(attrIter->name);
- if (sdkVersion > options.maxSdkAttribute.value()) {
- // We will silently omit this attribute
- smallestStrippedAttributeSdk =
- std::min(smallestStrippedAttributeSdk, sdkVersion);
- continue;
- }
- }
-
- ResourceNameRef genIdName;
- bool create = false;
- bool privateRef = false;
- if (mTable && ResourceParser::tryParseReference(attrIter->value, &genIdName,
- &create, &privateRef) && create) {
- mTable->addResource(genIdName, {}, source.line(parser->getLineNumber()),
- util::make_unique<Id>());
- }
-
-
- Maybe<std::u16string> package = util::extractPackageFromNamespace(
- attrIter->namespaceUri);
- if (!package || !mResolver) {
- // Attributes that have no resource ID (because they don't belong to a
- // package) should appear after those that do have resource IDs. Assign
- // them some integer value that will appear after.
- id = 0x80000000u | nextAttributeId++;
- nameRef = pool.makeRef(attrIter->name, StringPool::Context{ id });
-
- } else {
- // Find the Attribute object via our Resolver.
- ResourceName attrName = {
- package.value(), ResourceType::kAttr, attrIter->name };
-
- if (attrName.package.empty()) {
- attrName.package = options.defaultPackage;
- }
-
- Maybe<IResolver::Entry> result = mResolver->findAttribute(attrName);
- if (!result || !result.value().id.isValid()) {
- logger.error(parser->getLineNumber())
- << "unresolved attribute '"
- << attrName
- << "'."
- << std::endl;
- error = true;
- continue;
- }
-
- if (!result.value().attr) {
- logger.error(parser->getLineNumber())
- << "not a valid attribute '"
- << attrName
- << "'."
- << std::endl;
- error = true;
- continue;
- }
-
- id = result.value().id.id;
- attr = result.value().attr;
-
- // Put the attribute name into a package specific pool, since we don't
- // want to collapse names from different packages.
- nameRef = packagePools[package.value()].makeRef(
- attrIter->name, StringPool::Context{ id });
- }
-
- // Insert the attribute into the sorted vector.
- auto iter = std::lower_bound(sortedAttributes.begin(), sortedAttributes.end(),
- id, lessAttributeId);
- sortedAttributes.insert(iter, XmlAttribute{ id, &*attrIter, attr, nameRef });
- }
-
- if (error) {
- break;
- }
-
- // Now that we have filtered out some attributes, get the final count.
- elem->attributeCount = sortedAttributes.size();
-
- // Flatten the sorted attributes.
- uint16_t attributeIndex = 1;
- for (auto entry : sortedAttributes) {
- android::ResXMLTree_attribute* attr =
- out.nextBlock<android::ResXMLTree_attribute>();
- if (!entry.xmlAttr->namespaceUri.empty()) {
- stringRefs.emplace_back(
- pool.makeRef(entry.xmlAttr->namespaceUri, lowPriority), &attr->ns);
- } else {
- attr->ns.index = -1;
- }
-
- StringPool::Ref rawValueRef = pool.makeRef(entry.xmlAttr->value, lowPriority);
-
- stringRefs.emplace_back(entry.nameRef, &attr->name);
-
- if (options.keepRawValues) {
- stringRefs.emplace_back(rawValueRef, &attr->rawValue);
- } else {
- attr->rawValue.index = -1;
- }
-
- // Assign the indices for specific attributes.
- if (entry.xmlAttr->namespaceUri == kSchemaAndroid &&
- entry.xmlAttr->name == u"id") {
- elem->idIndex = attributeIndex;
- } else if (entry.xmlAttr->namespaceUri.empty()) {
- if (entry.xmlAttr->name == u"class") {
- elem->classIndex = attributeIndex;
- } else if (entry.xmlAttr->name == u"style") {
- elem->styleIndex = attributeIndex;
- }
- }
- attributeIndex++;
-
- std::unique_ptr<Item> value;
- if (entry.attr) {
- value = ResourceParser::parseItemForAttribute(entry.xmlAttr->value,
- *entry.attr);
- } else {
- bool create = false;
- value = ResourceParser::tryParseReference(entry.xmlAttr->value, &create);
- }
-
- if (mResolver && value) {
- AttributeValueFlattener flattener(
- mResolver,
- &logger,
- &attr->typedValue,
- parser,
- &error,
- rawValueRef,
- &options.defaultPackage,
- &stringRefs);
- value->accept(flattener, {});
- } else if (!value && entry.attr &&
- !(entry.attr->typeMask & android::ResTable_map::TYPE_STRING)) {
- logger.error(parser->getLineNumber())
- << "'"
- << *rawValueRef
- << "' is not compatible with attribute "
- << *entry.attr
- << "."
- << std::endl;
- error = true;
- } else {
- attr->typedValue.dataType = android::Res_value::TYPE_STRING;
- if (!options.keepRawValues) {
- // Don't set the string twice.
- stringRefs.emplace_back(rawValueRef, &attr->rawValue);
- }
- stringRefs.emplace_back(rawValueRef,
- reinterpret_cast<android::ResStringPool_ref*>(
- &attr->typedValue.data));
- }
- attr->typedValue.size = sizeof(attr->typedValue);
- }
-
- out.align4();
- node->header.size = out.size() - startIndex;
- break;
- }
-
- case XmlPullParser::Event::kEndElement: {
- const size_t startIndex = out.size();
- android::ResXMLTree_node* node = out.nextBlock<android::ResXMLTree_node>();
- node->header.type = android::RES_XML_END_ELEMENT_TYPE;
- node->header.headerSize = sizeof(*node);
- node->lineNumber = parser->getLineNumber();
- node->comment.index = -1;
-
- android::ResXMLTree_endElementExt* elem =
- out.nextBlock<android::ResXMLTree_endElementExt>();
- stringRefs.emplace_back(
- pool.makeRef(parser->getElementNamespace(), lowPriority), &elem->ns);
- stringRefs.emplace_back(
- pool.makeRef(parser->getElementName(), lowPriority), &elem->name);
-
- out.align4();
- node->header.size = out.size() - startIndex;
- break;
- }
-
- case XmlPullParser::Event::kText: {
- StringPiece16 text = util::trimWhitespace(parser->getText());
- if (text.empty()) {
- break;
- }
-
- const size_t startIndex = out.size();
- android::ResXMLTree_node* node = out.nextBlock<android::ResXMLTree_node>();
- node->header.type = android::RES_XML_CDATA_TYPE;
- node->header.headerSize = sizeof(*node);
- node->lineNumber = parser->getLineNumber();
- node->comment.index = -1;
-
- android::ResXMLTree_cdataExt* elem = out.nextBlock<android::ResXMLTree_cdataExt>();
- stringRefs.emplace_back(pool.makeRef(text, lowPriority), &elem->data);
-
- out.align4();
- node->header.size = out.size() - startIndex;
- break;
- }
-
- default:
- break;
- }
-
- }
- out.align4();
-
- if (error) {
- return {};
- }
-
- if (parser->getEvent() == XmlPullParser::Event::kBadDocument) {
- logger.error(parser->getLineNumber())
- << parser->getLastError()
- << std::endl;
- return {};
- }
-
- // Merge the package pools into the main pool.
- for (auto& packagePoolEntry : packagePools) {
- pool.merge(std::move(packagePoolEntry.second));
- }
-
- // Sort so that attribute resource IDs show up first.
- pool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+static void flattenXml(StringPool* pool, FlatStringRefList* stringRefs, BigBuffer* outBuffer,
+ BigBuffer&& xmlTreeBuffer) {
+ // Sort the string pool so that attribute resource IDs show up first.
+ pool->sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
return a.context.priority < b.context.priority;
});
// Now we flatten the string pool references into the correct places.
- for (const auto& refEntry : stringRefs) {
+ for (const auto& refEntry : *stringRefs) {
refEntry.second->index = refEntry.first.getIndex();
}
@@ -463,36 +482,93 @@
header->header.headerSize = sizeof(*header);
// Flatten the StringPool.
- StringPool::flattenUtf16(outBuffer, pool);
+ StringPool::flattenUtf16(outBuffer, *pool);
// Write the array of resource IDs, indexed by StringPool order.
const size_t beforeResIdMapIndex = outBuffer->size();
android::ResChunk_header* resIdMapChunk = outBuffer->nextBlock<android::ResChunk_header>();
resIdMapChunk->type = android::RES_XML_RESOURCE_MAP_TYPE;
resIdMapChunk->headerSize = sizeof(*resIdMapChunk);
- for (const auto& str : pool) {
+ for (const auto& str : *pool) {
ResourceId id { str->context.priority };
- if (!id.isValid()) {
+ if (id.id == kLowPriority || !id.isValid()) {
// When we see the first non-resource ID,
// we're done.
break;
}
- uint32_t* flatId = outBuffer->nextBlock<uint32_t>();
- *flatId = id.id;
+ *outBuffer->nextBlock<uint32_t>() = id.id;
}
resIdMapChunk->size = outBuffer->size() - beforeResIdMapIndex;
// Move the temporary BigBuffer into outBuffer.
- outBuffer->appendBuffer(std::move(out));
-
+ outBuffer->appendBuffer(std::move(xmlTreeBuffer));
header->header.size = outBuffer->size() - beforeXmlTreeIndex;
-
- if (smallestStrippedAttributeSdk == std::numeric_limits<size_t>::max()) {
- // Nothing was stripped
- return 0u;
- }
- return smallestStrippedAttributeSdk;
}
+bool flatten(Node* root, const std::u16string& defaultPackage, BigBuffer* outBuffer) {
+ StringPool pool;
+
+ // This will hold the StringRefs and the location in which to write the index.
+ // Once we sort the StringPool, we can assign the updated indices
+ // to the correct data locations.
+ FlatStringRefList stringRefs;
+
+ // Since we don't know the size of the final StringPool, we write to this
+ // temporary BigBuffer, which we will append to outBuffer later.
+ BigBuffer out(1024);
+
+ CompileXmlFlattener flattener(&out, &pool, &stringRefs, defaultPackage);
+ root->accept(&flattener);
+
+ if (!flattener.success()) {
+ return false;
+ }
+
+ flattenXml(&pool, &stringRefs, outBuffer, std::move(out));
+ return true;
+};
+
+Maybe<size_t> flattenAndLink(const Source& source, Node* root,
+ const std::u16string& defaultPackage,
+ const std::shared_ptr<IResolver>& resolver,
+ const FlattenOptions& options, BigBuffer* outBuffer) {
+ SourceLogger logger(source);
+ StringPool pool;
+
+ // Attribute names are stored without packages, but we use
+ // their StringPool index to lookup their resource IDs.
+ // This will cause collisions, so we can't dedupe
+ // attribute names from different packages. We use separate
+ // pools that we later combine.
+ std::map<std::u16string, StringPool> packagePools;
+
+ FlatStringRefList stringRefs;
+
+ // Since we don't know the size of the final StringPool, we write to this
+ // temporary BigBuffer, which we will append to outBuffer later.
+ BigBuffer out(1024);
+
+ LinkedXmlFlattener flattener(&out, &pool, &packagePools, &stringRefs, defaultPackage, resolver,
+ &logger, options);
+ root->accept(&flattener);
+
+ if (!flattener.success()) {
+ return {};
+ }
+
+ // Merge the package pools into the main pool.
+ for (auto& packagePoolEntry : packagePools) {
+ pool.merge(std::move(packagePoolEntry.second));
+ }
+
+ flattenXml(&pool, &stringRefs, outBuffer, std::move(out));
+
+ if (flattener.getSmallestFilteredSdk()) {
+ return flattener.getSmallestFilteredSdk();
+ }
+ return 0;
+}
+
+} // namespace xml
} // namespace aapt
diff --git a/tools/aapt2/XmlFlattener.h b/tools/aapt2/XmlFlattener.h
index 2cfcc16..4ece0a3 100644
--- a/tools/aapt2/XmlFlattener.h
+++ b/tools/aapt2/XmlFlattener.h
@@ -21,64 +21,49 @@
#include "Maybe.h"
#include "Resolver.h"
#include "Source.h"
-#include "XmlPullParser.h"
+#include "XmlDom.h"
#include <string>
namespace aapt {
+namespace xml {
/**
* Flattens an XML file into a binary representation parseable by
- * the Android resource system. References to resources are checked
- * and string values are transformed to typed data where possible.
+ * the Android resource system.
*/
-class XmlFlattener {
-public:
- struct Options {
- /**
- * The package to use when a reference has no package specified
- * (or a namespace URI equals "http://schemas.android.com/apk/res-auto").
- */
- std::u16string defaultPackage;
+bool flatten(Node* root, const std::u16string& defaultPackage, BigBuffer* outBuffer);
- /**
- * If set, tells the XmlFlattener to strip out
- * attributes that have been introduced after
- * max SDK.
- */
- Maybe<size_t> maxSdkAttribute;
-
- /**
- * Setting this to true will keep the raw string value of
- * an attribute's value when it has been resolved.
- */
- bool keepRawValues = false;
- };
+/**
+ * Options for flattenAndLink.
+ */
+struct FlattenOptions {
+ /**
+ * Keep attribute raw string values along with typed values.
+ */
+ bool keepRawValues = false;
/**
- * Creates a flattener with a Resolver to resolve references
- * and attributes.
+ * If set, any attribute introduced in a later SDK will not be encoded.
*/
- XmlFlattener(const std::shared_ptr<ResourceTable>& table,
- const std::shared_ptr<IResolver>& resolver);
-
- XmlFlattener(const XmlFlattener&) = delete; // Not copyable.
-
- /**
- * Flatten an XML file, reading from the XML parser and writing to the
- * BigBuffer. The source object is mainly for logging errors. If the
- * function succeeds, returns the smallest SDK version of an attribute that
- * was stripped out. If no attributes were stripped out, the return value
- * is 0.
- */
- Maybe<size_t> flatten(const Source& source, const std::shared_ptr<XmlPullParser>& parser,
- BigBuffer* outBuffer, Options options);
-
-private:
- std::shared_ptr<ResourceTable> mTable;
- std::shared_ptr<IResolver> mResolver;
+ Maybe<size_t> maxSdkAttribute;
};
+/**
+ * Like flatten(Node*,BigBuffer*), but references to resources are checked
+ * and string values are transformed to typed data where possible.
+ *
+ * `defaultPackage` is used when a reference has no package or the namespace URI
+ * "http://schemas.android.com/apk/res-auto" is used.
+ *
+ * `resolver` is used to resolve references to resources.
+ */
+Maybe<size_t> flattenAndLink(const Source& source, Node* root,
+ const std::u16string& defaultPackage,
+ const std::shared_ptr<IResolver>& resolver,
+ const FlattenOptions& options, BigBuffer* outBuffer);
+
+} // namespace xml
} // namespace aapt
#endif // AAPT_XML_FLATTENER_H
diff --git a/tools/aapt2/XmlFlattener_test.cpp b/tools/aapt2/XmlFlattener_test.cpp
index b45cd9b..8915d24 100644
--- a/tools/aapt2/XmlFlattener_test.cpp
+++ b/tools/aapt2/XmlFlattener_test.cpp
@@ -17,7 +17,6 @@
#include "MockResolver.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
-#include "SourceXmlPullParser.h"
#include "Util.h"
#include "XmlFlattener.h"
@@ -30,13 +29,14 @@
using namespace android;
namespace aapt {
+namespace xml {
constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
class XmlFlattenerTest : public ::testing::Test {
public:
virtual void SetUp() override {
- std::shared_ptr<IResolver> resolver = std::make_shared<MockResolver>(
+ mResolver = std::make_shared<MockResolver>(
std::make_shared<ResourceTable>(),
std::map<ResourceName, ResourceId>({
{ ResourceName{ u"android", ResourceType::kAttr, u"attr" },
@@ -47,18 +47,21 @@
ResourceId{ 0x01010001u } },
{ ResourceName{ u"com.lib", ResourceType::kId, u"id" },
ResourceId{ 0x01020001u } }}));
-
- mFlattener = std::make_shared<XmlFlattener>(nullptr, resolver);
}
::testing::AssertionResult testFlatten(const std::string& in, ResXMLTree* outTree) {
std::stringstream input(kXmlPreamble);
input << in << std::endl;
- std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(input);
+
+ SourceLogger logger(Source{ "test.xml" });
+ std::unique_ptr<Node> root = inflate(&input, &logger);
+ if (!root) {
+ return ::testing::AssertionFailure();
+ }
+
BigBuffer outBuffer(1024);
- XmlFlattener::Options xmlOptions;
- xmlOptions.defaultPackage = u"android";
- if (!mFlattener->flatten(Source{ "test" }, xmlParser, &outBuffer, xmlOptions)) {
+ if (!flattenAndLink(Source{ "test.xml" }, root.get(), std::u16string(u"android"),
+ mResolver, {}, &outBuffer)) {
return ::testing::AssertionFailure();
}
@@ -69,16 +72,48 @@
return ::testing::AssertionSuccess();
}
- std::shared_ptr<XmlFlattener> mFlattener;
+ std::shared_ptr<IResolver> mResolver;
};
TEST_F(XmlFlattenerTest, ParseSimpleView) {
- std::string input = "<View xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
- " android:attr=\"@id/id\">\n"
- "</View>";
+ std::string input = R"EOF(
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:attr="@id/id"
+ class="str"
+ style="@id/id">
+ </View>
+ )EOF";
ResXMLTree tree;
ASSERT_TRUE(testFlatten(input, &tree));
+ while (tree.next() != ResXMLTree::START_TAG) {
+ ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT);
+ ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
+ }
+
+ const StringPiece16 androidNs = u"http://schemas.android.com/apk/res/android";
+ const StringPiece16 attrName = u"attr";
+ ssize_t idx = tree.indexOfAttribute(androidNs.data(), androidNs.size(), attrName.data(),
+ attrName.size());
+ ASSERT_GE(idx, 0);
+ EXPECT_EQ(tree.getAttributeNameResID(idx), 0x01010000u);
+ EXPECT_EQ(tree.getAttributeDataType(idx), android::Res_value::TYPE_REFERENCE);
+
+ const StringPiece16 class16 = u"class";
+ idx = tree.indexOfAttribute(nullptr, 0, class16.data(), class16.size());
+ ASSERT_GE(idx, 0);
+ EXPECT_EQ(tree.getAttributeNameResID(idx), 0u);
+ EXPECT_EQ(tree.getAttributeDataType(idx), android::Res_value::TYPE_STRING);
+ EXPECT_EQ(tree.getAttributeData(idx), tree.getAttributeValueStringID(idx));
+
+ const StringPiece16 style16 = u"style";
+ idx = tree.indexOfAttribute(nullptr, 0, style16.data(), style16.size());
+ ASSERT_GE(idx, 0);
+ EXPECT_EQ(tree.getAttributeNameResID(idx), 0u);
+ EXPECT_EQ(tree.getAttributeDataType(idx), android::Res_value::TYPE_REFERENCE);
+ EXPECT_EQ((uint32_t) tree.getAttributeData(idx), 0x01020000u);
+ EXPECT_EQ(tree.getAttributeValueStringID(idx), -1);
+
while (tree.next() != ResXMLTree::END_DOCUMENT) {
ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
}
@@ -193,4 +228,5 @@
EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), 0);
}
+} // namespace xml
} // namespace aapt